Header And Logo

PostgreSQL
| The world's most advanced open source database.

oid2name.c

Go to the documentation of this file.
00001 /*
00002  * oid2name, a PostgreSQL app to map OIDs on the filesystem
00003  * to table and database names.
00004  *
00005  * Originally by
00006  * B. Palmer, [email protected] 1-17-2001
00007  *
00008  * contrib/oid2name/oid2name.c
00009  */
00010 #include "postgres_fe.h"
00011 
00012 #include <unistd.h>
00013 #ifdef HAVE_GETOPT_H
00014 #include <getopt.h>
00015 #endif
00016 
00017 extern char *optarg;
00018 
00019 #include "libpq-fe.h"
00020 
00021 /* an extensible array to keep track of elements to show */
00022 typedef struct
00023 {
00024     char      **array;
00025     int         num;
00026     int         alloc;
00027 } eary;
00028 
00029 /* these are the opts structures for command line params */
00030 struct options
00031 {
00032     eary       *tables;
00033     eary       *oids;
00034     eary       *filenodes;
00035 
00036     bool        quiet;
00037     bool        systables;
00038     bool        indexes;
00039     bool        nodb;
00040     bool        extended;
00041     bool        tablespaces;
00042 
00043     char       *dbname;
00044     char       *hostname;
00045     char       *port;
00046     char       *username;
00047     const char *progname;
00048 };
00049 
00050 /* function prototypes */
00051 static void help(const char *progname);
00052 void        get_opts(int, char **, struct options *);
00053 void        add_one_elt(char *eltname, eary *eary);
00054 char       *get_comma_elts(eary *eary);
00055 PGconn     *sql_conn(struct options *);
00056 int         sql_exec(PGconn *, const char *sql, bool quiet);
00057 void        sql_exec_dumpalldbs(PGconn *, struct options *);
00058 void        sql_exec_dumpalltables(PGconn *, struct options *);
00059 void        sql_exec_searchtables(PGconn *, struct options *);
00060 void        sql_exec_dumpalltbspc(PGconn *, struct options *);
00061 
00062 /* function to parse command line options and check for some usage errors. */
00063 void
00064 get_opts(int argc, char **argv, struct options * my_opts)
00065 {
00066     int         c;
00067     const char *progname;
00068 
00069     progname = get_progname(argv[0]);
00070 
00071     /* set the defaults */
00072     my_opts->quiet = false;
00073     my_opts->systables = false;
00074     my_opts->indexes = false;
00075     my_opts->nodb = false;
00076     my_opts->extended = false;
00077     my_opts->tablespaces = false;
00078     my_opts->dbname = NULL;
00079     my_opts->hostname = NULL;
00080     my_opts->port = NULL;
00081     my_opts->username = NULL;
00082     my_opts->progname = progname;
00083 
00084     if (argc > 1)
00085     {
00086         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
00087         {
00088             help(progname);
00089             exit(0);
00090         }
00091         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
00092         {
00093             puts("oid2name (PostgreSQL) " PG_VERSION);
00094             exit(0);
00095         }
00096     }
00097 
00098     /* get opts */
00099     while ((c = getopt(argc, argv, "H:p:U:d:t:o:f:qSxish")) != -1)
00100     {
00101         switch (c)
00102         {
00103                 /* specify the database */
00104             case 'd':
00105                 my_opts->dbname = pg_strdup(optarg);
00106                 break;
00107 
00108                 /* specify one tablename to show */
00109             case 't':
00110                 add_one_elt(optarg, my_opts->tables);
00111                 break;
00112 
00113                 /* specify one Oid to show */
00114             case 'o':
00115                 add_one_elt(optarg, my_opts->oids);
00116                 break;
00117 
00118                 /* specify one filenode to show */
00119             case 'f':
00120                 add_one_elt(optarg, my_opts->filenodes);
00121                 break;
00122 
00123                 /* don't show headers */
00124             case 'q':
00125                 my_opts->quiet = true;
00126                 break;
00127 
00128                 /* host to connect to */
00129             case 'H':
00130                 my_opts->hostname = pg_strdup(optarg);
00131                 break;
00132 
00133                 /* port to connect to on remote host */
00134             case 'p':
00135                 my_opts->port = pg_strdup(optarg);
00136                 break;
00137 
00138                 /* username */
00139             case 'U':
00140                 my_opts->username = pg_strdup(optarg);
00141                 break;
00142 
00143                 /* display system tables */
00144             case 'S':
00145                 my_opts->systables = true;
00146                 break;
00147 
00148                 /* also display indexes */
00149             case 'i':
00150                 my_opts->indexes = true;
00151                 break;
00152 
00153                 /* display extra columns */
00154             case 'x':
00155                 my_opts->extended = true;
00156                 break;
00157 
00158                 /* dump tablespaces only */
00159             case 's':
00160                 my_opts->tablespaces = true;
00161                 break;
00162 
00163             case 'h':
00164                 help(progname);
00165                 exit(0);
00166                 break;
00167 
00168             default:
00169                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
00170                 exit(1);
00171         }
00172     }
00173 }
00174 
00175 static void
00176 help(const char *progname)
00177 {
00178     printf("%s helps examining the file structure used by PostgreSQL.\n\n"
00179            "Usage:\n"
00180            "  %s [OPTION]...\n"
00181            "\nOptions:\n"
00182            "  -d DBNAME      database to connect to\n"
00183            "  -f FILENODE    show info for table with given file node\n"
00184            "  -H HOSTNAME    database server host or socket directory\n"
00185            "  -i             show indexes and sequences too\n"
00186            "  -o OID         show info for table with given OID\n"
00187            "  -p PORT        database server port number\n"
00188            "  -q             quiet (don't show headers)\n"
00189            "  -s             show all tablespaces\n"
00190            "  -S             show system objects too\n"
00191            "  -t TABLE       show info for named table\n"
00192            "  -U NAME        connect as specified database user\n"
00193            "  -V, --version  output version information, then exit\n"
00194            "  -x             extended (show additional columns)\n"
00195            "  -?, --help     show this help, then exit\n"
00196            "\nThe default action is to show all database OIDs.\n\n"
00197            "Report bugs to <[email protected]>.\n",
00198            progname, progname);
00199 }
00200 
00201 /*
00202  * add_one_elt
00203  *
00204  * Add one element to a (possibly empty) eary struct.
00205  */
00206 void
00207 add_one_elt(char *eltname, eary *eary)
00208 {
00209     if (eary->alloc == 0)
00210     {
00211         eary      ->alloc = 8;
00212         eary      ->array = (char **) pg_malloc(8 * sizeof(char *));
00213     }
00214     else if (eary->num >= eary->alloc)
00215     {
00216         eary      ->alloc *= 2;
00217         eary      ->array = (char **) pg_realloc(eary->array,
00218                                                  eary->alloc * sizeof(char *));
00219     }
00220 
00221     eary      ->array[eary->num] = pg_strdup(eltname);
00222     eary      ->num++;
00223 }
00224 
00225 /*
00226  * get_comma_elts
00227  *
00228  * Return the elements of an eary as a (freshly allocated) single string, in
00229  * single quotes, separated by commas and properly escaped for insertion in an
00230  * SQL statement.
00231  */
00232 char *
00233 get_comma_elts(eary *eary)
00234 {
00235     char       *ret,
00236                *ptr;
00237     int         i,
00238                 length = 0;
00239 
00240     if (eary->num == 0)
00241         return pg_strdup("");
00242 
00243     /*
00244      * PQescapeString wants 2 * length + 1 bytes of breath space.  Add two
00245      * chars per element for the single quotes and one for the comma.
00246      */
00247     for (i = 0; i < eary->num; i++)
00248         length += strlen(eary->array[i]);
00249 
00250     ret = (char *) pg_malloc(length * 2 + 4 * eary->num);
00251     ptr = ret;
00252 
00253     for (i = 0; i < eary->num; i++)
00254     {
00255         if (i != 0)
00256             sprintf(ptr++, ",");
00257         sprintf(ptr++, "'");
00258         ptr += PQescapeString(ptr, eary->array[i], strlen(eary->array[i]));
00259         sprintf(ptr++, "'");
00260     }
00261 
00262     return ret;
00263 }
00264 
00265 /* establish connection with database. */
00266 PGconn *
00267 sql_conn(struct options * my_opts)
00268 {
00269     PGconn     *conn;
00270     char       *password = NULL;
00271     bool        new_pass;
00272 
00273     /*
00274      * Start the connection.  Loop until we have a password if requested by
00275      * backend.
00276      */
00277     do
00278     {
00279 #define PARAMS_ARRAY_SIZE   7
00280 
00281         const char *keywords[PARAMS_ARRAY_SIZE];
00282         const char *values[PARAMS_ARRAY_SIZE];
00283 
00284         keywords[0] = "host";
00285         values[0] = my_opts->hostname;
00286         keywords[1] = "port";
00287         values[1] = my_opts->port;
00288         keywords[2] = "user";
00289         values[2] = my_opts->username;
00290         keywords[3] = "password";
00291         values[3] = password;
00292         keywords[4] = "dbname";
00293         values[4] = my_opts->dbname;
00294         keywords[5] = "fallback_application_name";
00295         values[5] = my_opts->progname;
00296         keywords[6] = NULL;
00297         values[6] = NULL;
00298 
00299         new_pass = false;
00300         conn = PQconnectdbParams(keywords, values, true);
00301 
00302         if (!conn)
00303         {
00304             fprintf(stderr, "%s: could not connect to database %s\n",
00305                     "oid2name", my_opts->dbname);
00306             exit(1);
00307         }
00308 
00309         if (PQstatus(conn) == CONNECTION_BAD &&
00310             PQconnectionNeedsPassword(conn) &&
00311             password == NULL)
00312         {
00313             PQfinish(conn);
00314             password = simple_prompt("Password: ", 100, false);
00315             new_pass = true;
00316         }
00317     } while (new_pass);
00318 
00319     if (password)
00320         free(password);
00321 
00322     /* check to see that the backend connection was successfully made */
00323     if (PQstatus(conn) == CONNECTION_BAD)
00324     {
00325         fprintf(stderr, "%s: could not connect to database %s: %s",
00326                 "oid2name", my_opts->dbname, PQerrorMessage(conn));
00327         PQfinish(conn);
00328         exit(1);
00329     }
00330 
00331     /* return the conn if good */
00332     return conn;
00333 }
00334 
00335 /*
00336  * Actual code to make call to the database and print the output data.
00337  */
00338 int
00339 sql_exec(PGconn *conn, const char *todo, bool quiet)
00340 {
00341     PGresult   *res;
00342 
00343     int         nfields;
00344     int         nrows;
00345     int         i,
00346                 j,
00347                 l;
00348     int        *length;
00349     char       *pad;
00350 
00351     /* make the call */
00352     res = PQexec(conn, todo);
00353 
00354     /* check and deal with errors */
00355     if (!res || PQresultStatus(res) > 2)
00356     {
00357         fprintf(stderr, "oid2name: query failed: %s\n", PQerrorMessage(conn));
00358         fprintf(stderr, "oid2name: query was: %s\n", todo);
00359 
00360         PQclear(res);
00361         PQfinish(conn);
00362         exit(-1);
00363     }
00364 
00365     /* get the number of fields */
00366     nrows = PQntuples(res);
00367     nfields = PQnfields(res);
00368 
00369     /* for each field, get the needed width */
00370     length = (int *) pg_malloc(sizeof(int) * nfields);
00371     for (j = 0; j < nfields; j++)
00372         length[j] = strlen(PQfname(res, j));
00373 
00374     for (i = 0; i < nrows; i++)
00375     {
00376         for (j = 0; j < nfields; j++)
00377         {
00378             l = strlen(PQgetvalue(res, i, j));
00379             if (l > length[j])
00380                 length[j] = strlen(PQgetvalue(res, i, j));
00381         }
00382     }
00383 
00384     /* print a header */
00385     if (!quiet)
00386     {
00387         for (j = 0, l = 0; j < nfields; j++)
00388         {
00389             fprintf(stdout, "%*s", length[j] + 2, PQfname(res, j));
00390             l += length[j] + 2;
00391         }
00392         fprintf(stdout, "\n");
00393         pad = (char *) pg_malloc(l + 1);
00394         MemSet(pad, '-', l);
00395         pad[l] = '\0';
00396         fprintf(stdout, "%s\n", pad);
00397         free(pad);
00398     }
00399 
00400     /* for each row, dump the information */
00401     for (i = 0; i < nrows; i++)
00402     {
00403         for (j = 0; j < nfields; j++)
00404             fprintf(stdout, "%*s", length[j] + 2, PQgetvalue(res, i, j));
00405         fprintf(stdout, "\n");
00406     }
00407 
00408     /* cleanup */
00409     PQclear(res);
00410     free(length);
00411 
00412     return 0;
00413 }
00414 
00415 /*
00416  * Dump all databases.  There are no system objects to worry about.
00417  */
00418 void
00419 sql_exec_dumpalldbs(PGconn *conn, struct options * opts)
00420 {
00421     char        todo[1024];
00422 
00423     /* get the oid and database name from the system pg_database table */
00424     snprintf(todo, sizeof(todo),
00425              "SELECT d.oid AS \"Oid\", datname AS \"Database Name\", "
00426              "spcname AS \"Tablespace\" FROM pg_catalog.pg_database d JOIN pg_catalog.pg_tablespace t ON "
00427              "(dattablespace = t.oid) ORDER BY 2");
00428 
00429     sql_exec(conn, todo, opts->quiet);
00430 }
00431 
00432 /*
00433  * Dump all tables, indexes and sequences in the current database.
00434  */
00435 void
00436 sql_exec_dumpalltables(PGconn *conn, struct options * opts)
00437 {
00438     char        todo[1024];
00439     char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
00440 
00441     snprintf(todo, sizeof(todo),
00442              "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s "
00443              "FROM pg_class c "
00444            "    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
00445              "  LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),"
00446              "  pg_catalog.pg_tablespace t "
00447              "WHERE relkind IN ('r', 'm'%s%s) AND "
00448              "  %s"
00449              "      t.oid = CASE"
00450              "          WHEN reltablespace <> 0 THEN reltablespace"
00451              "          ELSE dattablespace"
00452              "      END "
00453              "ORDER BY relname",
00454              opts->extended ? addfields : "",
00455              opts->indexes ? ", 'i', 'S'" : "",
00456              opts->systables ? ", 't'" : "",
00457              opts->systables ? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND");
00458 
00459     sql_exec(conn, todo, opts->quiet);
00460 }
00461 
00462 /*
00463  * Show oid, filenode, name, schema and tablespace for each of the
00464  * given objects in the current database.
00465  */
00466 void
00467 sql_exec_searchtables(PGconn *conn, struct options * opts)
00468 {
00469     char       *todo;
00470     char       *qualifiers,
00471                *ptr;
00472     char       *comma_oids,
00473                *comma_filenodes,
00474                *comma_tables;
00475     bool        written = false;
00476     char       *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
00477 
00478     /* get tables qualifiers, whether names, filenodes, or OIDs */
00479     comma_oids = get_comma_elts(opts->oids);
00480     comma_tables = get_comma_elts(opts->tables);
00481     comma_filenodes = get_comma_elts(opts->filenodes);
00482 
00483     /* 80 extra chars for SQL expression */
00484     qualifiers = (char *) pg_malloc(strlen(comma_oids) + strlen(comma_tables) +
00485                                     strlen(comma_filenodes) + 80);
00486     ptr = qualifiers;
00487 
00488     if (opts->oids->num > 0)
00489     {
00490         ptr += sprintf(ptr, "c.oid IN (%s)", comma_oids);
00491         written = true;
00492     }
00493     if (opts->filenodes->num > 0)
00494     {
00495         if (written)
00496             ptr += sprintf(ptr, " OR ");
00497         ptr += sprintf(ptr, "pg_catalog.pg_relation_filenode(c.oid) IN (%s)", comma_filenodes);
00498         written = true;
00499     }
00500     if (opts->tables->num > 0)
00501     {
00502         if (written)
00503             ptr += sprintf(ptr, " OR ");
00504         sprintf(ptr, "c.relname ~~ ANY (ARRAY[%s])", comma_tables);
00505     }
00506     free(comma_oids);
00507     free(comma_tables);
00508     free(comma_filenodes);
00509 
00510     /* now build the query */
00511     todo = (char *) pg_malloc(650 + strlen(qualifiers));
00512     snprintf(todo, 650 + strlen(qualifiers),
00513              "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s\n"
00514              "FROM pg_catalog.pg_class c \n"
00515          "  LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \n"
00516              "  LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n"
00517              "  pg_catalog.pg_tablespace t \n"
00518              "WHERE relkind IN ('r', 'm', 'i', 'S', 't') AND \n"
00519              "      t.oid = CASE\n"
00520              "          WHEN reltablespace <> 0 THEN reltablespace\n"
00521              "          ELSE dattablespace\n"
00522              "      END AND \n"
00523              "  (%s) \n"
00524              "ORDER BY relname\n",
00525              opts->extended ? addfields : "",
00526              qualifiers);
00527 
00528     free(qualifiers);
00529 
00530     sql_exec(conn, todo, opts->quiet);
00531 }
00532 
00533 void
00534 sql_exec_dumpalltbspc(PGconn *conn, struct options * opts)
00535 {
00536     char        todo[1024];
00537 
00538     snprintf(todo, sizeof(todo),
00539              "SELECT oid AS \"Oid\", spcname as \"Tablespace Name\"\n"
00540              "FROM pg_catalog.pg_tablespace");
00541 
00542     sql_exec(conn, todo, opts->quiet);
00543 }
00544 
00545 int
00546 main(int argc, char **argv)
00547 {
00548     struct options *my_opts;
00549     PGconn     *pgconn;
00550 
00551     my_opts = (struct options *) pg_malloc(sizeof(struct options));
00552 
00553     my_opts->oids = (eary *) pg_malloc(sizeof(eary));
00554     my_opts->tables = (eary *) pg_malloc(sizeof(eary));
00555     my_opts->filenodes = (eary *) pg_malloc(sizeof(eary));
00556 
00557     my_opts->oids->num = my_opts->oids->alloc = 0;
00558     my_opts->tables->num = my_opts->tables->alloc = 0;
00559     my_opts->filenodes->num = my_opts->filenodes->alloc = 0;
00560 
00561     /* parse the opts */
00562     get_opts(argc, argv, my_opts);
00563 
00564     if (my_opts->dbname == NULL)
00565     {
00566         my_opts->dbname = "postgres";
00567         my_opts->nodb = true;
00568     }
00569     pgconn = sql_conn(my_opts);
00570 
00571     /* display only tablespaces */
00572     if (my_opts->tablespaces)
00573     {
00574         if (!my_opts->quiet)
00575             printf("All tablespaces:\n");
00576         sql_exec_dumpalltbspc(pgconn, my_opts);
00577 
00578         PQfinish(pgconn);
00579         exit(0);
00580     }
00581 
00582     /* display the given elements in the database */
00583     if (my_opts->oids->num > 0 ||
00584         my_opts->tables->num > 0 ||
00585         my_opts->filenodes->num > 0)
00586     {
00587         if (!my_opts->quiet)
00588             printf("From database \"%s\":\n", my_opts->dbname);
00589         sql_exec_searchtables(pgconn, my_opts);
00590 
00591         PQfinish(pgconn);
00592         exit(0);
00593     }
00594 
00595     /* no elements given; dump the given database */
00596     if (my_opts->dbname && !my_opts->nodb)
00597     {
00598         if (!my_opts->quiet)
00599             printf("From database \"%s\":\n", my_opts->dbname);
00600         sql_exec_dumpalltables(pgconn, my_opts);
00601 
00602         PQfinish(pgconn);
00603         exit(0);
00604     }
00605 
00606     /* no database either; dump all databases */
00607     if (!my_opts->quiet)
00608         printf("All databases:\n");
00609     sql_exec_dumpalldbs(pgconn, my_opts);
00610 
00611     PQfinish(pgconn);
00612     return 0;
00613 }