]> granicus.if.org Git - postgresql/blob - contrib/oid2name/oid2name.c
Standardize naming of malloc/realloc/strdup wrapper functions.
[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       *pg_malloc(size_t size);
54 char       *pg_strdup(const char *str);
55 void            add_one_elt(char *eltname, eary *eary);
56 char       *get_comma_elts(eary *eary);
57 PGconn     *sql_conn(struct options *);
58 int                     sql_exec(PGconn *, const char *sql, bool quiet);
59 void            sql_exec_dumpalldbs(PGconn *, struct options *);
60 void            sql_exec_dumpalltables(PGconn *, struct options *);
61 void            sql_exec_searchtables(PGconn *, struct options *);
62 void            sql_exec_dumpalltbspc(PGconn *, struct options *);
63
64 /* function to parse command line options and check for some usage errors. */
65 void
66 get_opts(int argc, char **argv, struct options * my_opts)
67 {
68         int                     c;
69         const char *progname;
70
71         progname = get_progname(argv[0]);
72
73         /* set the defaults */
74         my_opts->quiet = false;
75         my_opts->systables = false;
76         my_opts->indexes = false;
77         my_opts->nodb = false;
78         my_opts->extended = false;
79         my_opts->tablespaces = false;
80         my_opts->dbname = NULL;
81         my_opts->hostname = NULL;
82         my_opts->port = NULL;
83         my_opts->username = NULL;
84         my_opts->progname = progname;
85
86         if (argc > 1)
87         {
88                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
89                 {
90                         help(progname);
91                         exit(0);
92                 }
93                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
94                 {
95                         puts("oid2name (PostgreSQL) " PG_VERSION);
96                         exit(0);
97                 }
98         }
99
100         /* get opts */
101         while ((c = getopt(argc, argv, "H:p:U:d:t:o:f:qSxish")) != -1)
102         {
103                 switch (c)
104                 {
105                                 /* specify the database */
106                         case 'd':
107                                 my_opts->dbname = pg_strdup(optarg);
108                                 break;
109
110                                 /* specify one tablename to show */
111                         case 't':
112                                 add_one_elt(optarg, my_opts->tables);
113                                 break;
114
115                                 /* specify one Oid to show */
116                         case 'o':
117                                 add_one_elt(optarg, my_opts->oids);
118                                 break;
119
120                                 /* specify one filenode to show */
121                         case 'f':
122                                 add_one_elt(optarg, my_opts->filenodes);
123                                 break;
124
125                                 /* don't show headers */
126                         case 'q':
127                                 my_opts->quiet = true;
128                                 break;
129
130                                 /* host to connect to */
131                         case 'H':
132                                 my_opts->hostname = pg_strdup(optarg);
133                                 break;
134
135                                 /* port to connect to on remote host */
136                         case 'p':
137                                 my_opts->port = pg_strdup(optarg);
138                                 break;
139
140                                 /* username */
141                         case 'U':
142                                 my_opts->username = pg_strdup(optarg);
143                                 break;
144
145                                 /* display system tables */
146                         case 'S':
147                                 my_opts->systables = true;
148                                 break;
149
150                                 /* also display indexes */
151                         case 'i':
152                                 my_opts->indexes = true;
153                                 break;
154
155                                 /* display extra columns */
156                         case 'x':
157                                 my_opts->extended = true;
158                                 break;
159
160                                 /* dump tablespaces only */
161                         case 's':
162                                 my_opts->tablespaces = true;
163                                 break;
164
165                         case 'h':
166                                 help(progname);
167                                 exit(0);
168                                 break;
169
170                         default:
171                                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
172                                 exit(1);
173                 }
174         }
175 }
176
177 static void
178 help(const char *progname)
179 {
180         printf("%s helps examining the file structure used by PostgreSQL.\n\n"
181                    "Usage:\n"
182                    "  %s [OPTION]...\n"
183                    "\nOptions:\n"
184                    "  -d DBNAME      database to connect to\n"
185                    "  -f FILENODE    show info for table with given file node\n"
186                    "  -H HOSTNAME    database server host or socket directory\n"
187                    "  -i             show indexes and sequences too\n"
188                    "  -o OID         show info for table with given OID\n"
189                    "  -p PORT        database server port number\n"
190                    "  -q             quiet (don't show headers)\n"
191                    "  -s             show all tablespaces\n"
192                    "  -S             show system objects too\n"
193                    "  -t TABLE       show info for named table\n"
194                    "  -U NAME        connect as specified database user\n"
195                    "  -V, --version  output version information, then exit\n"
196                    "  -x             extended (show additional columns)\n"
197                    "  -?, --help     show this help, then exit\n"
198                    "\nThe default action is to show all database OIDs.\n\n"
199                    "Report bugs to <pgsql-bugs@postgresql.org>.\n",
200                    progname, progname);
201 }
202
203 void *
204 pg_malloc(size_t size)
205 {
206         void       *ptr = malloc(size);
207
208         if (!ptr)
209         {
210                 fprintf(stderr, "out of memory");
211                 exit(1);
212         }
213         return ptr;
214 }
215
216 char *
217 pg_strdup(const char *str)
218 {
219         char       *result = strdup(str);
220
221         if (!result)
222         {
223                 fprintf(stderr, "out of memory");
224                 exit(1);
225         }
226         return result;
227 }
228
229 /*
230  * add_one_elt
231  *
232  * Add one element to a (possibly empty) eary struct.
233  */
234 void
235 add_one_elt(char *eltname, eary *eary)
236 {
237         if (eary->alloc == 0)
238         {
239                 eary      ->alloc = 8;
240                 eary      ->array = (char **) pg_malloc(8 * sizeof(char *));
241         }
242         else if (eary->num >= eary->alloc)
243         {
244                 eary      ->alloc *= 2;
245                 eary      ->array = (char **)
246                 realloc(eary->array, eary->alloc * sizeof(char *));
247
248                 if (!eary->array)
249                 {
250                         fprintf(stderr, "out of memory");
251                         exit(1);
252                 }
253         }
254
255         eary      ->array[eary->num] = pg_strdup(eltname);
256         eary      ->num++;
257 }
258
259 /*
260  * get_comma_elts
261  *
262  * Return the elements of an eary as a (freshly allocated) single string, in
263  * single quotes, separated by commas and properly escaped for insertion in an
264  * SQL statement.
265  */
266 char *
267 get_comma_elts(eary *eary)
268 {
269         char       *ret,
270                            *ptr;
271         int                     i,
272                                 length = 0;
273
274         if (eary->num == 0)
275                 return pg_strdup("");
276
277         /*
278          * PQescapeString wants 2 * length + 1 bytes of breath space.  Add two
279          * chars per element for the single quotes and one for the comma.
280          */
281         for (i = 0; i < eary->num; i++)
282                 length += strlen(eary->array[i]);
283
284         ret = (char *) pg_malloc(length * 2 + 4 * eary->num);
285         ptr = ret;
286
287         for (i = 0; i < eary->num; i++)
288         {
289                 if (i != 0)
290                         sprintf(ptr++, ",");
291                 sprintf(ptr++, "'");
292                 ptr += PQescapeString(ptr, eary->array[i], strlen(eary->array[i]));
293                 sprintf(ptr++, "'");
294         }
295
296         return ret;
297 }
298
299 /* establish connection with database. */
300 PGconn *
301 sql_conn(struct options * my_opts)
302 {
303         PGconn     *conn;
304         char       *password = NULL;
305         bool            new_pass;
306
307         /*
308          * Start the connection.  Loop until we have a password if requested by
309          * backend.
310          */
311         do
312         {
313 #define PARAMS_ARRAY_SIZE       7
314
315                 const char *keywords[PARAMS_ARRAY_SIZE];
316                 const char *values[PARAMS_ARRAY_SIZE];
317
318                 keywords[0] = "host";
319                 values[0] = my_opts->hostname;
320                 keywords[1] = "port";
321                 values[1] = my_opts->port;
322                 keywords[2] = "user";
323                 values[2] = my_opts->username;
324                 keywords[3] = "password";
325                 values[3] = password;
326                 keywords[4] = "dbname";
327                 values[4] = my_opts->dbname;
328                 keywords[5] = "fallback_application_name";
329                 values[5] = my_opts->progname;
330                 keywords[6] = NULL;
331                 values[6] = NULL;
332
333                 new_pass = false;
334                 conn = PQconnectdbParams(keywords, values, true);
335
336                 if (!conn)
337                 {
338                         fprintf(stderr, "%s: could not connect to database %s\n",
339                                         "oid2name", my_opts->dbname);
340                         exit(1);
341                 }
342
343                 if (PQstatus(conn) == CONNECTION_BAD &&
344                         PQconnectionNeedsPassword(conn) &&
345                         password == NULL)
346                 {
347                         PQfinish(conn);
348                         password = simple_prompt("Password: ", 100, false);
349                         new_pass = true;
350                 }
351         } while (new_pass);
352
353         if (password)
354                 free(password);
355
356         /* check to see that the backend connection was successfully made */
357         if (PQstatus(conn) == CONNECTION_BAD)
358         {
359                 fprintf(stderr, "%s: could not connect to database %s: %s",
360                                 "oid2name", my_opts->dbname, PQerrorMessage(conn));
361                 PQfinish(conn);
362                 exit(1);
363         }
364
365         /* return the conn if good */
366         return conn;
367 }
368
369 /*
370  * Actual code to make call to the database and print the output data.
371  */
372 int
373 sql_exec(PGconn *conn, const char *todo, bool quiet)
374 {
375         PGresult   *res;
376
377         int                     nfields;
378         int                     nrows;
379         int                     i,
380                                 j,
381                                 l;
382         int                *length;
383         char       *pad;
384
385         /* make the call */
386         res = PQexec(conn, todo);
387
388         /* check and deal with errors */
389         if (!res || PQresultStatus(res) > 2)
390         {
391                 fprintf(stderr, "oid2name: query failed: %s\n", PQerrorMessage(conn));
392                 fprintf(stderr, "oid2name: query was: %s\n", todo);
393
394                 PQclear(res);
395                 PQfinish(conn);
396                 exit(-1);
397         }
398
399         /* get the number of fields */
400         nrows = PQntuples(res);
401         nfields = PQnfields(res);
402
403         /* for each field, get the needed width */
404         length = (int *) pg_malloc(sizeof(int) * nfields);
405         for (j = 0; j < nfields; j++)
406                 length[j] = strlen(PQfname(res, j));
407
408         for (i = 0; i < nrows; i++)
409         {
410                 for (j = 0; j < nfields; j++)
411                 {
412                         l = strlen(PQgetvalue(res, i, j));
413                         if (l > length[j])
414                                 length[j] = strlen(PQgetvalue(res, i, j));
415                 }
416         }
417
418         /* print a header */
419         if (!quiet)
420         {
421                 for (j = 0, l = 0; j < nfields; j++)
422                 {
423                         fprintf(stdout, "%*s", length[j] + 2, PQfname(res, j));
424                         l += length[j] + 2;
425                 }
426                 fprintf(stdout, "\n");
427                 pad = (char *) pg_malloc(l + 1);
428                 MemSet(pad, '-', l);
429                 pad[l] = '\0';
430                 fprintf(stdout, "%s\n", pad);
431                 free(pad);
432         }
433
434         /* for each row, dump the information */
435         for (i = 0; i < nrows; i++)
436         {
437                 for (j = 0; j < nfields; j++)
438                         fprintf(stdout, "%*s", length[j] + 2, PQgetvalue(res, i, j));
439                 fprintf(stdout, "\n");
440         }
441
442         /* cleanup */
443         PQclear(res);
444         free(length);
445
446         return 0;
447 }
448
449 /*
450  * Dump all databases.  There are no system objects to worry about.
451  */
452 void
453 sql_exec_dumpalldbs(PGconn *conn, struct options * opts)
454 {
455         char            todo[1024];
456
457         /* get the oid and database name from the system pg_database table */
458         snprintf(todo, sizeof(todo),
459                          "SELECT d.oid AS \"Oid\", datname AS \"Database Name\", "
460                          "spcname AS \"Tablespace\" FROM pg_catalog.pg_database d JOIN pg_catalog.pg_tablespace t ON "
461                          "(dattablespace = t.oid) ORDER BY 2");
462
463         sql_exec(conn, todo, opts->quiet);
464 }
465
466 /*
467  * Dump all tables, indexes and sequences in the current database.
468  */
469 void
470 sql_exec_dumpalltables(PGconn *conn, struct options * opts)
471 {
472         char            todo[1024];
473         char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
474
475         snprintf(todo, sizeof(todo),
476                          "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s "
477                          "FROM pg_class c "
478                    "    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
479                          "      LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),"
480                          "      pg_catalog.pg_tablespace t "
481                          "WHERE relkind IN ('r'%s%s) AND "
482                          "      %s"
483                          "              t.oid = CASE"
484                          "                      WHEN reltablespace <> 0 THEN reltablespace"
485                          "                      ELSE dattablespace"
486                          "              END "
487                          "ORDER BY relname",
488                          opts->extended ? addfields : "",
489                          opts->indexes ? ", 'i', 'S'" : "",
490                          opts->systables ? ", 't'" : "",
491                          opts->systables ? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND");
492
493         sql_exec(conn, todo, opts->quiet);
494 }
495
496 /*
497  * Show oid, filenode, name, schema and tablespace for each of the
498  * given objects in the current database.
499  */
500 void
501 sql_exec_searchtables(PGconn *conn, struct options * opts)
502 {
503         char       *todo;
504         char       *qualifiers,
505                            *ptr;
506         char       *comma_oids,
507                            *comma_filenodes,
508                            *comma_tables;
509         bool            written = false;
510         char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
511
512         /* get tables qualifiers, whether names, filenodes, or OIDs */
513         comma_oids = get_comma_elts(opts->oids);
514         comma_tables = get_comma_elts(opts->tables);
515         comma_filenodes = get_comma_elts(opts->filenodes);
516
517         /* 80 extra chars for SQL expression */
518         qualifiers = (char *) pg_malloc(strlen(comma_oids) + strlen(comma_tables) +
519                                                                         strlen(comma_filenodes) + 80);
520         ptr = qualifiers;
521
522         if (opts->oids->num > 0)
523         {
524                 ptr += sprintf(ptr, "c.oid IN (%s)", comma_oids);
525                 written = true;
526         }
527         if (opts->filenodes->num > 0)
528         {
529                 if (written)
530                         ptr += sprintf(ptr, " OR ");
531                 ptr += sprintf(ptr, "pg_catalog.pg_relation_filenode(c.oid) IN (%s)", comma_filenodes);
532                 written = true;
533         }
534         if (opts->tables->num > 0)
535         {
536                 if (written)
537                         ptr += sprintf(ptr, " OR ");
538                 sprintf(ptr, "c.relname ~~ ANY (ARRAY[%s])", comma_tables);
539         }
540         free(comma_oids);
541         free(comma_tables);
542         free(comma_filenodes);
543
544         /* now build the query */
545         todo = (char *) pg_malloc(650 + strlen(qualifiers));
546         snprintf(todo, 650 + strlen(qualifiers),
547                          "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s\n"
548                          "FROM pg_catalog.pg_class c \n"
549                  "      LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \n"
550                          "      LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n"
551                          "      pg_catalog.pg_tablespace t \n"
552                          "WHERE relkind IN ('r', 'i', 'S', 't') AND \n"
553                          "              t.oid = CASE\n"
554                          "                      WHEN reltablespace <> 0 THEN reltablespace\n"
555                          "                      ELSE dattablespace\n"
556                          "              END AND \n"
557                          "  (%s) \n"
558                          "ORDER BY relname\n",
559                          opts->extended ? addfields : "",
560                          qualifiers);
561
562         free(qualifiers);
563
564         sql_exec(conn, todo, opts->quiet);
565 }
566
567 void
568 sql_exec_dumpalltbspc(PGconn *conn, struct options * opts)
569 {
570         char            todo[1024];
571
572         snprintf(todo, sizeof(todo),
573                          "SELECT oid AS \"Oid\", spcname as \"Tablespace Name\"\n"
574                          "FROM pg_catalog.pg_tablespace");
575
576         sql_exec(conn, todo, opts->quiet);
577 }
578
579 int
580 main(int argc, char **argv)
581 {
582         struct options *my_opts;
583         PGconn     *pgconn;
584
585         my_opts = (struct options *) pg_malloc(sizeof(struct options));
586
587         my_opts->oids = (eary *) pg_malloc(sizeof(eary));
588         my_opts->tables = (eary *) pg_malloc(sizeof(eary));
589         my_opts->filenodes = (eary *) pg_malloc(sizeof(eary));
590
591         my_opts->oids->num = my_opts->oids->alloc = 0;
592         my_opts->tables->num = my_opts->tables->alloc = 0;
593         my_opts->filenodes->num = my_opts->filenodes->alloc = 0;
594
595         /* parse the opts */
596         get_opts(argc, argv, my_opts);
597
598         if (my_opts->dbname == NULL)
599         {
600                 my_opts->dbname = "postgres";
601                 my_opts->nodb = true;
602         }
603         pgconn = sql_conn(my_opts);
604
605         /* display only tablespaces */
606         if (my_opts->tablespaces)
607         {
608                 if (!my_opts->quiet)
609                         printf("All tablespaces:\n");
610                 sql_exec_dumpalltbspc(pgconn, my_opts);
611
612                 PQfinish(pgconn);
613                 exit(0);
614         }
615
616         /* display the given elements in the database */
617         if (my_opts->oids->num > 0 ||
618                 my_opts->tables->num > 0 ||
619                 my_opts->filenodes->num > 0)
620         {
621                 if (!my_opts->quiet)
622                         printf("From database \"%s\":\n", my_opts->dbname);
623                 sql_exec_searchtables(pgconn, my_opts);
624
625                 PQfinish(pgconn);
626                 exit(0);
627         }
628
629         /* no elements given; dump the given database */
630         if (my_opts->dbname && !my_opts->nodb)
631         {
632                 if (!my_opts->quiet)
633                         printf("From database \"%s\":\n", my_opts->dbname);
634                 sql_exec_dumpalltables(pgconn, my_opts);
635
636                 PQfinish(pgconn);
637                 exit(0);
638         }
639
640         /* no database either; dump all databases */
641         if (!my_opts->quiet)
642                 printf("All databases:\n");
643         sql_exec_dumpalldbs(pgconn, my_opts);
644
645         PQfinish(pgconn);
646         return 0;
647 }