]> granicus.if.org Git - postgresql/blob - contrib/oid2name/oid2name.c
Fix the inconsistent and wrong claims that the option value in CREATE
[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  * $PostgreSQL: pgsql/contrib/oid2name/oid2name.c,v 1.34 2009/02/25 13:24:40 petere Exp $
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 };
48
49 /* function prototypes */
50 void            get_opts(int, char **, struct options *);
51 void       *myalloc(size_t size);
52 char       *mystrdup(const char *str);
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
68         /* set the defaults */
69         my_opts->quiet = false;
70         my_opts->systables = false;
71         my_opts->indexes = false;
72         my_opts->nodb = false;
73         my_opts->extended = false;
74         my_opts->tablespaces = false;
75         my_opts->dbname = NULL;
76         my_opts->hostname = NULL;
77         my_opts->port = NULL;
78         my_opts->username = NULL;
79
80         /* get opts */
81         while ((c = getopt(argc, argv, "H:p:U:d:t:o:f:qSxish?")) != -1)
82         {
83                 switch (c)
84                 {
85                                 /* specify the database */
86                         case 'd':
87                                 my_opts->dbname = mystrdup(optarg);
88                                 break;
89
90                                 /* specify one tablename to show */
91                         case 't':
92                                 add_one_elt(optarg, my_opts->tables);
93                                 break;
94
95                                 /* specify one Oid to show */
96                         case 'o':
97                                 add_one_elt(optarg, my_opts->oids);
98                                 break;
99
100                                 /* specify one filenode to show */
101                         case 'f':
102                                 add_one_elt(optarg, my_opts->filenodes);
103                                 break;
104
105                                 /* don't show headers */
106                         case 'q':
107                                 my_opts->quiet = true;
108                                 break;
109
110                                 /* host to connect to */
111                         case 'H':
112                                 my_opts->hostname = mystrdup(optarg);
113                                 break;
114
115                                 /* port to connect to on remote host */
116                         case 'p':
117                                 my_opts->port = mystrdup(optarg);
118                                 break;
119
120                                 /* username */
121                         case 'U':
122                                 my_opts->username = mystrdup(optarg);
123                                 break;
124
125                                 /* display system tables */
126                         case 'S':
127                                 my_opts->systables = true;
128                                 break;
129
130                                 /* also display indexes */
131                         case 'i':
132                                 my_opts->indexes = true;
133                                 break;
134
135                                 /* display extra columns */
136                         case 'x':
137                                 my_opts->extended = true;
138                                 break;
139
140                                 /* dump tablespaces only */
141                         case 's':
142                                 my_opts->tablespaces = true;
143                                 break;
144
145                                 /* help! (ugly in code for easier editing) */
146                         case '?':
147                         case 'h':
148                                 fprintf(stderr,
149                                                 "Usage: oid2name [-s|-d database] [-S][-i][-q][-x] [-t table|-o oid|-f file] ...\n"
150                                          "        default action        show all database Oids\n"
151                                          "        -d database           database to connect to\n"
152                                                 "        -s                    show all tablespaces\n"
153                                         "        -S                    show system objects too\n"
154                                                 "        -i                    show indexes and sequences too\n"
155                                                 "        -x                    extended (show additional columns)\n"
156                                  "        -q                    quiet (don't show headers)\n"
157                                                 "        -t <table>            show info for table named <table>\n"
158                                                 "        -o <oid>              show info for table with Oid <oid>\n"
159                                                 "        -f <filenode>         show info for table with filenode <filenode>\n"
160                                          "        -H host               connect to remote host\n"
161                                         "        -p port               host port to connect to\n"
162                                    "        -U username           username to connect with\n"
163                                         );
164                                 exit(1);
165                                 break;
166                 }
167         }
168 }
169
170 void *
171 myalloc(size_t size)
172 {
173         void       *ptr = malloc(size);
174
175         if (!ptr)
176         {
177                 fprintf(stderr, "out of memory");
178                 exit(1);
179         }
180         return ptr;
181 }
182
183 char *
184 mystrdup(const char *str)
185 {
186         char       *result = strdup(str);
187
188         if (!result)
189         {
190                 fprintf(stderr, "out of memory");
191                 exit(1);
192         }
193         return result;
194 }
195
196 /*
197  * add_one_elt
198  *
199  * Add one element to a (possibly empty) eary struct.
200  */
201 void
202 add_one_elt(char *eltname, eary * eary)
203 {
204         if (eary->alloc == 0)
205         {
206                 eary->alloc = 8;
207                 eary->array = (char **) myalloc(8 * sizeof(char *));
208         }
209         else if (eary->num >= eary->alloc)
210         {
211                 eary->alloc *= 2;
212                 eary->array = (char **)
213                         realloc(eary->array, eary->alloc * sizeof(char *));
214                 if (!eary->array)
215                 {
216                         fprintf(stderr, "out of memory");
217                         exit(1);
218                 }
219         }
220
221         eary->array[eary->num] = mystrdup(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 mystrdup("");
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 *) myalloc(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                 new_pass = false;
280                 conn = PQsetdbLogin(my_opts->hostname,
281                                                         my_opts->port,
282                                                         NULL,   /* options */
283                                                         NULL,   /* tty */
284                                                         my_opts->dbname,
285                                                         my_opts->username,
286                                                         password);
287                 if (!conn)
288                 {
289                         fprintf(stderr, "%s: could not connect to database %s\n",
290                                         "oid2name", my_opts->dbname);
291                         exit(1);
292                 }
293
294                 if (PQstatus(conn) == CONNECTION_BAD &&
295                         PQconnectionNeedsPassword(conn) &&
296                         password == NULL)
297                 {
298                         PQfinish(conn);
299                         password = simple_prompt("Password: ", 100, false);
300                         new_pass = true;
301                 }
302         } while (new_pass);
303
304         if (password)
305                 free(password);
306
307         /* check to see that the backend connection was successfully made */
308         if (PQstatus(conn) == CONNECTION_BAD)
309         {
310                 fprintf(stderr, "%s: could not connect to database %s: %s",
311                                 "oid2name", my_opts->dbname, PQerrorMessage(conn));
312                 PQfinish(conn);
313                 exit(1);
314         }
315
316         /* return the conn if good */
317         return conn;
318 }
319
320 /*
321  * Actual code to make call to the database and print the output data.
322  */
323 int
324 sql_exec(PGconn *conn, const char *todo, bool quiet)
325 {
326         PGresult   *res;
327
328         int                     nfields;
329         int                     nrows;
330         int                     i,
331                                 j,
332                                 l;
333         int                *length;
334         char       *pad;
335
336         /* make the call */
337         res = PQexec(conn, todo);
338
339         /* check and deal with errors */
340         if (!res || PQresultStatus(res) > 2)
341         {
342                 fprintf(stderr, "oid2name: query failed: %s\n", PQerrorMessage(conn));
343                 fprintf(stderr, "oid2name: query was: %s\n", todo);
344
345                 PQclear(res);
346                 PQfinish(conn);
347                 exit(-1);
348         }
349
350         /* get the number of fields */
351         nrows = PQntuples(res);
352         nfields = PQnfields(res);
353
354         /* for each field, get the needed width */
355         length = (int *) myalloc(sizeof(int) * nfields);
356         for (j = 0; j < nfields; j++)
357                 length[j] = strlen(PQfname(res, j));
358
359         for (i = 0; i < nrows; i++)
360         {
361                 for (j = 0; j < nfields; j++)
362                 {
363                         l = strlen(PQgetvalue(res, i, j));
364                         if (l > length[j])
365                                 length[j] = strlen(PQgetvalue(res, i, j));
366                 }
367         }
368
369         /* print a header */
370         if (!quiet)
371         {
372                 for (j = 0, l = 0; j < nfields; j++)
373                 {
374                         fprintf(stdout, "%*s", length[j] + 2, PQfname(res, j));
375                         l += length[j] + 2;
376                 }
377                 fprintf(stdout, "\n");
378                 pad = (char *) myalloc(l + 1);
379                 MemSet(pad, '-', l);
380                 pad[l] = '\0';
381                 fprintf(stdout, "%s\n", pad);
382                 free(pad);
383         }
384
385         /* for each row, dump the information */
386         for (i = 0; i < nrows; i++)
387         {
388                 for (j = 0; j < nfields; j++)
389                         fprintf(stdout, "%*s", length[j] + 2, PQgetvalue(res, i, j));
390                 fprintf(stdout, "\n");
391         }
392
393         /* cleanup */
394         PQclear(res);
395         free(length);
396
397         return 0;
398 }
399
400 /*
401  * Dump all databases.  There are no system objects to worry about.
402  */
403 void
404 sql_exec_dumpalldbs(PGconn *conn, struct options * opts)
405 {
406         char            todo[1024];
407
408         /* get the oid and database name from the system pg_database table */
409         snprintf(todo, sizeof(todo),
410                          "SELECT d.oid AS \"Oid\", datname AS \"Database Name\", "
411           "spcname AS \"Tablespace\" FROM pg_database d JOIN pg_tablespace t ON "
412                          "(dattablespace = t.oid) ORDER BY 2");
413
414         sql_exec(conn, todo, opts->quiet);
415 }
416
417 /*
418  * Dump all tables, indexes and sequences in the current database.
419  */
420 void
421 sql_exec_dumpalltables(PGconn *conn, struct options * opts)
422 {
423         char            todo[1024];
424         char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
425
426         snprintf(todo, sizeof(todo),
427                   "SELECT relfilenode as \"Filenode\", relname as \"Table Name\" %s "
428                          "FROM pg_class c "
429                    "    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
430         "       LEFT JOIN pg_catalog.pg_database d ON d.datname = current_database(),"
431                          "      pg_catalog.pg_tablespace t "
432                          "WHERE relkind IN ('r'%s%s) AND "
433                          "      %s"
434                          "              t.oid = CASE"
435                          "                      WHEN reltablespace <> 0 THEN reltablespace"
436                          "                      ELSE dattablespace"
437                          "              END "
438                          "ORDER BY relname",
439                          opts->extended ? addfields : "",
440                          opts->indexes ? ", 'i', 'S'" : "",
441                          opts->systables ? ", 't'" : "",
442                          opts->systables ? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND");
443
444         sql_exec(conn, todo, opts->quiet);
445 }
446
447 /*
448  * Show oid, relfilenode, name, schema and tablespace for each of the
449  * given objects in the current database.
450  */
451 void
452 sql_exec_searchtables(PGconn *conn, struct options * opts)
453 {
454         char       *todo;
455         char       *qualifiers,
456                            *ptr;
457         char       *comma_oids,
458                            *comma_filenodes,
459                            *comma_tables;
460         bool            written = false;
461         char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
462
463         /* get tables qualifiers, whether names, relfilenodes, or OIDs */
464         comma_oids = get_comma_elts(opts->oids);
465         comma_tables = get_comma_elts(opts->tables);
466         comma_filenodes = get_comma_elts(opts->filenodes);
467
468         /* 80 extra chars for SQL expression */
469         qualifiers = (char *) myalloc(strlen(comma_oids) + strlen(comma_tables) +
470                                                                   strlen(comma_filenodes) + 80);
471         ptr = qualifiers;
472
473         if (opts->oids->num > 0)
474         {
475                 ptr += sprintf(ptr, "c.oid IN (%s)", comma_oids);
476                 written = true;
477         }
478         if (opts->filenodes->num > 0)
479         {
480                 if (written)
481                         ptr += sprintf(ptr, " OR ");
482                 ptr += sprintf(ptr, "c.relfilenode IN (%s)", comma_filenodes);
483                 written = true;
484         }
485         if (opts->tables->num > 0)
486         {
487                 if (written)
488                         ptr += sprintf(ptr, " OR ");
489                 sprintf(ptr, "c.relname ~~ ANY (ARRAY[%s])", comma_tables);
490         }
491         free(comma_oids);
492         free(comma_tables);
493         free(comma_filenodes);
494
495         /* now build the query */
496         todo = (char *) myalloc(650 + strlen(qualifiers));
497         snprintf(todo, 650 + strlen(qualifiers),
498                  "SELECT relfilenode as \"Filenode\", relname as \"Table Name\" %s\n"
499                          "FROM pg_class c \n"
500                  "      LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \n"
501                          "      LEFT JOIN pg_catalog.pg_database d ON d.datname = current_database(),\n"
502                          "      pg_catalog.pg_tablespace t \n"
503                          "WHERE relkind IN ('r', 'i', 'S', 't') AND \n"
504                          "              t.oid = CASE\n"
505                          "                      WHEN reltablespace <> 0 THEN reltablespace\n"
506                          "                      ELSE dattablespace\n"
507                          "              END AND \n"
508                          "  (%s) \n"
509                          "ORDER BY relname\n",
510                          opts->extended ? addfields : "",
511                          qualifiers);
512
513         free(qualifiers);
514
515         sql_exec(conn, todo, opts->quiet);
516 }
517
518 void
519 sql_exec_dumpalltbspc(PGconn *conn, struct options * opts)
520 {
521         char            todo[1024];
522
523         snprintf(todo, sizeof(todo),
524                          "SELECT oid AS \"Oid\", spcname as \"Tablespace Name\"\n"
525                          "FROM pg_tablespace");
526
527         sql_exec(conn, todo, opts->quiet);
528 }
529
530 int
531 main(int argc, char **argv)
532 {
533         struct options *my_opts;
534         PGconn     *pgconn;
535
536         my_opts = (struct options *) myalloc(sizeof(struct options));
537
538         my_opts->oids = (eary *) myalloc(sizeof(eary));
539         my_opts->tables = (eary *) myalloc(sizeof(eary));
540         my_opts->filenodes = (eary *) myalloc(sizeof(eary));
541
542         my_opts->oids->num = my_opts->oids->alloc = 0;
543         my_opts->tables->num = my_opts->tables->alloc = 0;
544         my_opts->filenodes->num = my_opts->filenodes->alloc = 0;
545
546         /* parse the opts */
547         get_opts(argc, argv, my_opts);
548
549         if (my_opts->dbname == NULL)
550         {
551                 my_opts->dbname = "postgres";
552                 my_opts->nodb = true;
553         }
554         pgconn = sql_conn(my_opts);
555
556         /* display only tablespaces */
557         if (my_opts->tablespaces)
558         {
559                 if (!my_opts->quiet)
560                         printf("All tablespaces:\n");
561                 sql_exec_dumpalltbspc(pgconn, my_opts);
562
563                 PQfinish(pgconn);
564                 exit(0);
565         }
566
567         /* display the given elements in the database */
568         if (my_opts->oids->num > 0 ||
569                 my_opts->tables->num > 0 ||
570                 my_opts->filenodes->num > 0)
571         {
572                 if (!my_opts->quiet)
573                         printf("From database \"%s\":\n", my_opts->dbname);
574                 sql_exec_searchtables(pgconn, my_opts);
575
576                 PQfinish(pgconn);
577                 exit(0);
578         }
579
580         /* no elements given; dump the given database */
581         if (my_opts->dbname && !my_opts->nodb)
582         {
583                 if (!my_opts->quiet)
584                         printf("From database \"%s\":\n", my_opts->dbname);
585                 sql_exec_dumpalltables(pgconn, my_opts);
586
587                 PQfinish(pgconn);
588                 exit(0);
589         }
590
591         /* no database either; dump all databases */
592         if (!my_opts->quiet)
593                 printf("All databases:\n");
594         sql_exec_dumpalldbs(pgconn, my_opts);
595
596         PQfinish(pgconn);
597         return 0;
598 }