]> granicus.if.org Git - postgresql/blob - contrib/oid2name/oid2name.c
Create libpgcommon, and move pg_malloc et al to it
[postgresql] / contrib / oid2name / oid2name.c
1 /*
2  * oid2name, a PostgreSQL app to map OIDs on the filesystem
3  * to table and database names.
4  *
5  * Originally by
6  * B. Palmer, bpalmer@crimelabs.net 1-17-2001
7  *
8  * contrib/oid2name/oid2name.c
9  */
10 #include "postgres_fe.h"
11
12 #include <unistd.h>
13 #ifdef HAVE_GETOPT_H
14 #include <getopt.h>
15 #endif
16
17 extern char *optarg;
18
19 #include "libpq-fe.h"
20
21 /* an extensible array to keep track of elements to show */
22 typedef struct
23 {
24         char      **array;
25         int                     num;
26         int                     alloc;
27 } eary;
28
29 /* these are the opts structures for command line params */
30 struct options
31 {
32         eary       *tables;
33         eary       *oids;
34         eary       *filenodes;
35
36         bool            quiet;
37         bool            systables;
38         bool            indexes;
39         bool            nodb;
40         bool            extended;
41         bool            tablespaces;
42
43         char       *dbname;
44         char       *hostname;
45         char       *port;
46         char       *username;
47         const char *progname;
48 };
49
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 *);
61
62 /* function to parse command line options and check for some usage errors. */
63 void
64 get_opts(int argc, char **argv, struct options * my_opts)
65 {
66         int                     c;
67         const char *progname;
68
69         progname = get_progname(argv[0]);
70
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;
80         my_opts->port = NULL;
81         my_opts->username = NULL;
82         my_opts->progname = progname;
83
84         if (argc > 1)
85         {
86                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
87                 {
88                         help(progname);
89                         exit(0);
90                 }
91                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
92                 {
93                         puts("oid2name (PostgreSQL) " PG_VERSION);
94                         exit(0);
95                 }
96         }
97
98         /* get opts */
99         while ((c = getopt(argc, argv, "H:p:U:d:t:o:f:qSxish")) != -1)
100         {
101                 switch (c)
102                 {
103                                 /* specify the database */
104                         case 'd':
105                                 my_opts->dbname = pg_strdup(optarg);
106                                 break;
107
108                                 /* specify one tablename to show */
109                         case 't':
110                                 add_one_elt(optarg, my_opts->tables);
111                                 break;
112
113                                 /* specify one Oid to show */
114                         case 'o':
115                                 add_one_elt(optarg, my_opts->oids);
116                                 break;
117
118                                 /* specify one filenode to show */
119                         case 'f':
120                                 add_one_elt(optarg, my_opts->filenodes);
121                                 break;
122
123                                 /* don't show headers */
124                         case 'q':
125                                 my_opts->quiet = true;
126                                 break;
127
128                                 /* host to connect to */
129                         case 'H':
130                                 my_opts->hostname = pg_strdup(optarg);
131                                 break;
132
133                                 /* port to connect to on remote host */
134                         case 'p':
135                                 my_opts->port = pg_strdup(optarg);
136                                 break;
137
138                                 /* username */
139                         case 'U':
140                                 my_opts->username = pg_strdup(optarg);
141                                 break;
142
143                                 /* display system tables */
144                         case 'S':
145                                 my_opts->systables = true;
146                                 break;
147
148                                 /* also display indexes */
149                         case 'i':
150                                 my_opts->indexes = true;
151                                 break;
152
153                                 /* display extra columns */
154                         case 'x':
155                                 my_opts->extended = true;
156                                 break;
157
158                                 /* dump tablespaces only */
159                         case 's':
160                                 my_opts->tablespaces = true;
161                                 break;
162
163                         case 'h':
164                                 help(progname);
165                                 exit(0);
166                                 break;
167
168                         default:
169                                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
170                                 exit(1);
171                 }
172         }
173 }
174
175 static void
176 help(const char *progname)
177 {
178         printf("%s helps examining the file structure used by PostgreSQL.\n\n"
179                    "Usage:\n"
180                    "  %s [OPTION]...\n"
181                    "\nOptions:\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",
198                    progname, progname);
199 }
200
201 /*
202  * add_one_elt
203  *
204  * Add one element to a (possibly empty) eary struct.
205  */
206 void
207 add_one_elt(char *eltname, eary *eary)
208 {
209         if (eary->alloc == 0)
210         {
211                 eary      ->alloc = 8;
212                 eary      ->array = (char **) pg_malloc(8 * sizeof(char *));
213         }
214         else if (eary->num >= eary->alloc)
215         {
216                 eary      ->alloc *= 2;
217                 eary      ->array = (char **) pg_realloc(eary->array,
218                                                                                                  eary->alloc * sizeof(char *));
219         }
220
221         eary      ->array[eary->num] = pg_strdup(eltname);
222         eary      ->num++;
223 }
224
225 /*
226  * get_comma_elts
227  *
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
230  * SQL statement.
231  */
232 char *
233 get_comma_elts(eary *eary)
234 {
235         char       *ret,
236                            *ptr;
237         int                     i,
238                                 length = 0;
239
240         if (eary->num == 0)
241                 return pg_strdup("");
242
243         /*
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.
246          */
247         for (i = 0; i < eary->num; i++)
248                 length += strlen(eary->array[i]);
249
250         ret = (char *) pg_malloc(length * 2 + 4 * eary->num);
251         ptr = ret;
252
253         for (i = 0; i < eary->num; i++)
254         {
255                 if (i != 0)
256                         sprintf(ptr++, ",");
257                 sprintf(ptr++, "'");
258                 ptr += PQescapeString(ptr, eary->array[i], strlen(eary->array[i]));
259                 sprintf(ptr++, "'");
260         }
261
262         return ret;
263 }
264
265 /* establish connection with database. */
266 PGconn *
267 sql_conn(struct options * my_opts)
268 {
269         PGconn     *conn;
270         char       *password = NULL;
271         bool            new_pass;
272
273         /*
274          * Start the connection.  Loop until we have a password if requested by
275          * backend.
276          */
277         do
278         {
279 #define PARAMS_ARRAY_SIZE       7
280
281                 const char *keywords[PARAMS_ARRAY_SIZE];
282                 const char *values[PARAMS_ARRAY_SIZE];
283
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;
296                 keywords[6] = NULL;
297                 values[6] = NULL;
298
299                 new_pass = false;
300                 conn = PQconnectdbParams(keywords, values, true);
301
302                 if (!conn)
303                 {
304                         fprintf(stderr, "%s: could not connect to database %s\n",
305                                         "oid2name", my_opts->dbname);
306                         exit(1);
307                 }
308
309                 if (PQstatus(conn) == CONNECTION_BAD &&
310                         PQconnectionNeedsPassword(conn) &&
311                         password == NULL)
312                 {
313                         PQfinish(conn);
314                         password = simple_prompt("Password: ", 100, false);
315                         new_pass = true;
316                 }
317         } while (new_pass);
318
319         if (password)
320                 free(password);
321
322         /* check to see that the backend connection was successfully made */
323         if (PQstatus(conn) == CONNECTION_BAD)
324         {
325                 fprintf(stderr, "%s: could not connect to database %s: %s",
326                                 "oid2name", my_opts->dbname, PQerrorMessage(conn));
327                 PQfinish(conn);
328                 exit(1);
329         }
330
331         /* return the conn if good */
332         return conn;
333 }
334
335 /*
336  * Actual code to make call to the database and print the output data.
337  */
338 int
339 sql_exec(PGconn *conn, const char *todo, bool quiet)
340 {
341         PGresult   *res;
342
343         int                     nfields;
344         int                     nrows;
345         int                     i,
346                                 j,
347                                 l;
348         int                *length;
349         char       *pad;
350
351         /* make the call */
352         res = PQexec(conn, todo);
353
354         /* check and deal with errors */
355         if (!res || PQresultStatus(res) > 2)
356         {
357                 fprintf(stderr, "oid2name: query failed: %s\n", PQerrorMessage(conn));
358                 fprintf(stderr, "oid2name: query was: %s\n", todo);
359
360                 PQclear(res);
361                 PQfinish(conn);
362                 exit(-1);
363         }
364
365         /* get the number of fields */
366         nrows = PQntuples(res);
367         nfields = PQnfields(res);
368
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));
373
374         for (i = 0; i < nrows; i++)
375         {
376                 for (j = 0; j < nfields; j++)
377                 {
378                         l = strlen(PQgetvalue(res, i, j));
379                         if (l > length[j])
380                                 length[j] = strlen(PQgetvalue(res, i, j));
381                 }
382         }
383
384         /* print a header */
385         if (!quiet)
386         {
387                 for (j = 0, l = 0; j < nfields; j++)
388                 {
389                         fprintf(stdout, "%*s", length[j] + 2, PQfname(res, j));
390                         l += length[j] + 2;
391                 }
392                 fprintf(stdout, "\n");
393                 pad = (char *) pg_malloc(l + 1);
394                 MemSet(pad, '-', l);
395                 pad[l] = '\0';
396                 fprintf(stdout, "%s\n", pad);
397                 free(pad);
398         }
399
400         /* for each row, dump the information */
401         for (i = 0; i < nrows; i++)
402         {
403                 for (j = 0; j < nfields; j++)
404                         fprintf(stdout, "%*s", length[j] + 2, PQgetvalue(res, i, j));
405                 fprintf(stdout, "\n");
406         }
407
408         /* cleanup */
409         PQclear(res);
410         free(length);
411
412         return 0;
413 }
414
415 /*
416  * Dump all databases.  There are no system objects to worry about.
417  */
418 void
419 sql_exec_dumpalldbs(PGconn *conn, struct options * opts)
420 {
421         char            todo[1024];
422
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");
428
429         sql_exec(conn, todo, opts->quiet);
430 }
431
432 /*
433  * Dump all tables, indexes and sequences in the current database.
434  */
435 void
436 sql_exec_dumpalltables(PGconn *conn, struct options * opts)
437 {
438         char            todo[1024];
439         char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
440
441         snprintf(todo, sizeof(todo),
442                          "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s "
443                          "FROM pg_class c "
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 "
448                          "      %s"
449                          "              t.oid = CASE"
450                          "                      WHEN reltablespace <> 0 THEN reltablespace"
451                          "                      ELSE dattablespace"
452                          "              END "
453                          "ORDER BY relname",
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");
458
459         sql_exec(conn, todo, opts->quiet);
460 }
461
462 /*
463  * Show oid, filenode, name, schema and tablespace for each of the
464  * given objects in the current database.
465  */
466 void
467 sql_exec_searchtables(PGconn *conn, struct options * opts)
468 {
469         char       *todo;
470         char       *qualifiers,
471                            *ptr;
472         char       *comma_oids,
473                            *comma_filenodes,
474                            *comma_tables;
475         bool            written = false;
476         char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
477
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);
482
483         /* 80 extra chars for SQL expression */
484         qualifiers = (char *) pg_malloc(strlen(comma_oids) + strlen(comma_tables) +
485                                                                         strlen(comma_filenodes) + 80);
486         ptr = qualifiers;
487
488         if (opts->oids->num > 0)
489         {
490                 ptr += sprintf(ptr, "c.oid IN (%s)", comma_oids);
491                 written = true;
492         }
493         if (opts->filenodes->num > 0)
494         {
495                 if (written)
496                         ptr += sprintf(ptr, " OR ");
497                 ptr += sprintf(ptr, "pg_catalog.pg_relation_filenode(c.oid) IN (%s)", comma_filenodes);
498                 written = true;
499         }
500         if (opts->tables->num > 0)
501         {
502                 if (written)
503                         ptr += sprintf(ptr, " OR ");
504                 sprintf(ptr, "c.relname ~~ ANY (ARRAY[%s])", comma_tables);
505         }
506         free(comma_oids);
507         free(comma_tables);
508         free(comma_filenodes);
509
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"
519                          "              t.oid = CASE\n"
520                          "                      WHEN reltablespace <> 0 THEN reltablespace\n"
521                          "                      ELSE dattablespace\n"
522                          "              END AND \n"
523                          "  (%s) \n"
524                          "ORDER BY relname\n",
525                          opts->extended ? addfields : "",
526                          qualifiers);
527
528         free(qualifiers);
529
530         sql_exec(conn, todo, opts->quiet);
531 }
532
533 void
534 sql_exec_dumpalltbspc(PGconn *conn, struct options * opts)
535 {
536         char            todo[1024];
537
538         snprintf(todo, sizeof(todo),
539                          "SELECT oid AS \"Oid\", spcname as \"Tablespace Name\"\n"
540                          "FROM pg_catalog.pg_tablespace");
541
542         sql_exec(conn, todo, opts->quiet);
543 }
544
545 int
546 main(int argc, char **argv)
547 {
548         struct options *my_opts;
549         PGconn     *pgconn;
550
551         my_opts = (struct options *) pg_malloc(sizeof(struct options));
552
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));
556
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;
560
561         /* parse the opts */
562         get_opts(argc, argv, my_opts);
563
564         if (my_opts->dbname == NULL)
565         {
566                 my_opts->dbname = "postgres";
567                 my_opts->nodb = true;
568         }
569         pgconn = sql_conn(my_opts);
570
571         /* display only tablespaces */
572         if (my_opts->tablespaces)
573         {
574                 if (!my_opts->quiet)
575                         printf("All tablespaces:\n");
576                 sql_exec_dumpalltbspc(pgconn, my_opts);
577
578                 PQfinish(pgconn);
579                 exit(0);
580         }
581
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)
586         {
587                 if (!my_opts->quiet)
588                         printf("From database \"%s\":\n", my_opts->dbname);
589                 sql_exec_searchtables(pgconn, my_opts);
590
591                 PQfinish(pgconn);
592                 exit(0);
593         }
594
595         /* no elements given; dump the given database */
596         if (my_opts->dbname && !my_opts->nodb)
597         {
598                 if (!my_opts->quiet)
599                         printf("From database \"%s\":\n", my_opts->dbname);
600                 sql_exec_dumpalltables(pgconn, my_opts);
601
602                 PQfinish(pgconn);
603                 exit(0);
604         }
605
606         /* no database either; dump all databases */
607         if (!my_opts->quiet)
608                 printf("All databases:\n");
609         sql_exec_dumpalldbs(pgconn, my_opts);
610
611         PQfinish(pgconn);
612         return 0;
613 }