diff options
Diffstat (limited to 'src/bin/psql/describe.c')
-rw-r--r-- | src/bin/psql/describe.c | 761 |
1 files changed, 485 insertions, 276 deletions
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6af2b06a8b5..b0b4f9d48d4 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -34,55 +34,60 @@ * takes an optional regexp to match specific aggregates by name */ bool -describeAggregates(const char *name, PsqlSettings *pset) +describeAggregates(const char *name, PsqlSettings *pset, bool verbose, bool desc) { - char descbuf[384 + 2 * REGEXP_CUTOFF]; /* observe/adjust this - * if you change the - * query */ + char buf[384 + 2 * REGEXP_CUTOFF]; PGresult *res; - bool description = GetVariableBool(pset->vars, "description"); printQueryOpt myopt = pset->popt; - descbuf[0] = '\0'; - /* * There are two kinds of aggregates: ones that work on particular * types ones that work on all */ - strcat(descbuf, + strcpy(buf, "SELECT a.aggname AS \"Name\", t.typname AS \"Type\""); - if (description) - strcat(descbuf, - ",\n obj_description(a.oid) as \"Description\""); - strcat(descbuf, - "\nFROM pg_aggregate a, pg_type t\n" - "WHERE a.aggbasetype = t.oid\n"); + if (verbose) + strcat(buf, " ,u.usename as \"Owner\""); + if (desc) + strcat(buf, ",\n obj_description(a.oid) as \"Description\""); + strcat(buf, !verbose ? + ("\nFROM pg_aggregate a, pg_type t\n" + "WHERE a.aggbasetype = t.oid\n") : + ("\nFROM pg_aggregate a, pg_type t, pg_user u\n" + "WHERE a.aggbasetype = t.oid AND a.aggowner = u.usesysid\n") + ); + if (name) { - strcat(descbuf, " AND a.aggname ~* '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND a.aggname ~* '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } - strcat(descbuf, + strcat(buf, "UNION\n" "SELECT a.aggname AS \"Name\", '(all types)' as \"Type\""); - if (description) - strcat(descbuf, + if (verbose) + strcat(buf, " ,u.usename as \"Owner\""); + if (desc) + strcat(buf, ",\n obj_description(a.oid) as \"Description\""); - strcat(descbuf, - "\nFROM pg_aggregate a\n" - "WHERE a.aggbasetype = 0\n"); + strcat(buf, !verbose ? + ("\nFROM pg_aggregate a\n" + "WHERE a.aggbasetype = 0\n") : + ("\nFROM pg_aggregate a, pg_user u\n" + "WHERE a.aggbasetype = 0 AND a.aggowner = u.usesysid\n") + ); if (name) { - strcat(descbuf, " AND a.aggname ~* '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND a.aggname ~* '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } - strcat(descbuf, "ORDER BY \"Name\", \"Type\""); + strcat(buf, "ORDER BY \"Name\", \"Type\""); - res = PSQLexec(pset, descbuf); + res = PSQLexec(pset, buf); if (!res) return false; @@ -98,12 +103,12 @@ describeAggregates(const char *name, PsqlSettings *pset) /* \df - * takes an optional regexp to narrow down the function name + * Takes an optional regexp to narrow down the function name */ bool -describeFunctions(const char *name, PsqlSettings *pset) +describeFunctions(const char *name, PsqlSettings *pset, bool verbose, bool desc) { - char descbuf[384 + REGEXP_CUTOFF]; + char buf[384 + REGEXP_CUTOFF]; PGresult *res; printQueryOpt myopt = pset->popt; @@ -111,23 +116,33 @@ describeFunctions(const char *name, PsqlSettings *pset) * we skip in/out funcs by excluding functions that take some * arguments, but have no types defined for those arguments */ - descbuf[0] = '\0'; - - strcat(descbuf, "SELECT t.typname as \"Result\", p.proname as \"Function\",\n" + strcpy(buf, + "SELECT t.typname as \"Result\", p.proname as \"Function\",\n" " oid8types(p.proargtypes) as \"Arguments\""); - if (GetVariableBool(pset->vars, "description")) - strcat(descbuf, "\n, obj_description(p.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_proc p, pg_type t\n" - "WHERE p.prorettype = t.oid and (pronargs = 0 or oid8types(p.proargtypes) != '')\n"); + if (verbose) + strcat(buf, ",\n u.usename as \"Owner\", l.lanname as \"Language\", p.prosrc as \"Source\""); + if (desc) + strcat(buf, ",\n obj_description(p.oid) as \"Description\""); + + if (!verbose) + strcat(buf, + "\nFROM pg_proc p, pg_type t\n" + "WHERE p.prorettype = t.oid and (pronargs = 0 or oid8types(p.proargtypes) != '')\n"); + else + strcat(buf, + "\nFROM pg_proc p, pg_type t, pg_language l, pg_user u\n" + "WHERE p.prorettype = t.oid AND p.prolang = l.oid AND p.proowner = u.usesysid\n" + " AND (pronargs = 0 or oid8types(p.proargtypes) != '')\n"); + if (name) { - strcat(descbuf, " AND p.proname ~* '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND p.proname ~* '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } - strcat(descbuf, "ORDER BY \"Function\", \"Result\", \"Arguments\""); + strcat(buf, "ORDER BY \"Function\", \"Result\", \"Arguments\""); - res = PSQLexec(pset, descbuf); + res = PSQLexec(pset, buf); if (!res) return false; @@ -144,33 +159,44 @@ describeFunctions(const char *name, PsqlSettings *pset) /* - * describeTypes - * - * for \dT + * \dT + * describe types */ bool -describeTypes(const char *name, PsqlSettings *pset) +describeTypes(const char *name, PsqlSettings *pset, bool verbose, bool desc) { - char descbuf[256 + REGEXP_CUTOFF]; + char buf[256 + REGEXP_CUTOFF]; PGresult *res; printQueryOpt myopt = pset->popt; - descbuf[0] = '\0'; - strcat(descbuf, "SELECT typname AS \"Type\""); - if (GetVariableBool(pset->vars, "description")) - strcat(descbuf, ", obj_description(oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_type\n" - "WHERE typrelid = 0 AND typname !~ '^_.*'\n"); + strcpy(buf, "SELECT t.typname AS \"Type\""); + if (verbose) + strcat(buf, + ",\n (CASE WHEN t.typlen=-1 THEN 'var'::text ELSE t.typlen::text END) as \"Length\"" + ",\n u.usename as \"Owner\"" + ); + if (desc) + strcat(buf, ",\n obj_description(t.oid) as \"Description\""); + /* + * do not include array types (start with underscore), + * do not include user relations (typrelid!=0) + */ + strcat(buf, !verbose ? + ("\nFROM pg_type t\n" + "WHERE t.typrelid = 0 AND t.typname !~ '^_.*'\n") : + ("\nFROM pg_type t, pg_user u\n" + "WHERE t.typrelid = 0 AND t.typname !~ '^_.*' AND t.typowner = u.usesysid\n") + ); if (name) { - strcat(descbuf, " AND typname ~* '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "' "); + strcat(buf, " AND t.typname ~* '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "' "); } - strcat(descbuf, "ORDER BY typname;"); + strcat(buf, "ORDER BY t.typname;"); - res = PSQLexec(pset, descbuf); + res = PSQLexec(pset, buf); if (!res) return false; @@ -187,28 +213,28 @@ describeTypes(const char *name, PsqlSettings *pset) /* \do - * NOTE: The (optional) argument here is _not_ a regexp since with all the - * funny chars floating around that would probably confuse people. It's an - * exact match string. */ bool -describeOperators(const char *name, PsqlSettings *pset) +describeOperators(const char *name, PsqlSettings *pset, bool verbose, bool desc) { - char descbuf[1536 + 3 * 32]; /* 32 is max length for operator - * name */ + char buf[1536 + 3 * REGEXP_CUTOFF]; PGresult *res; - bool description = GetVariableBool(pset->vars, "description"); printQueryOpt myopt = pset->popt; - descbuf[0] = '\0'; + /* Not used right now. Maybe later. */ + (void)verbose; - strcat(descbuf, "SELECT o.oprname AS \"Op\",\n" + /* FIXME: Use outer joins here when ready */ + + strcpy(buf, + "SELECT o.oprname AS \"Op\",\n" " t1.typname AS \"Left arg\",\n" " t2.typname AS \"Right arg\",\n" " t0.typname AS \"Result\""); - if (description) - strcat(descbuf, ",\n obj_description(p.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_proc p, pg_type t0,\n" + if (desc) + strcat(buf, ",\n obj_description(p.oid) as \"Description\""); + strcat(buf, + "\nFROM pg_proc p, pg_type t0,\n" " pg_type t1, pg_type t2,\n" " pg_operator o\n" "WHERE p.prorettype = t0.oid AND\n" @@ -218,51 +244,51 @@ describeOperators(const char *name, PsqlSettings *pset) " o.oprright = t2.oid\n"); if (name) { - strcat(descbuf, " AND o.oprname = '"); - strncat(descbuf, name, 32); - strcat(descbuf, "'\n"); + strcat(buf, " AND o.oprname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } - strcat(descbuf, "\nUNION\n\n" + strcat(buf, "\nUNION\n\n" "SELECT o.oprname as \"Op\",\n" " ''::name AS \"Left arg\",\n" " t1.typname AS \"Right arg\",\n" " t0.typname AS \"Result\""); - if (description) - strcat(descbuf, ",\n obj_description(p.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n" + if (desc) + strcat(buf, ",\n obj_description(p.oid) as \"Description\""); + strcat(buf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n" "WHERE RegprocToOid(o.oprcode) = p.oid AND\n" " o.oprresult = t0.oid AND\n" " o.oprkind = 'l' AND\n" " o.oprright = t1.oid\n"); if (name) { - strcat(descbuf, "AND o.oprname = '"); - strncat(descbuf, name, 32); - strcat(descbuf, "'\n"); + strcat(buf, "AND o.oprname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } - strcat(descbuf, "\nUNION\n\n" + strcat(buf, "\nUNION\n\n" "SELECT o.oprname as \"Op\",\n" " t1.typname AS \"Left arg\",\n" " ''::name AS \"Right arg\",\n" " t0.typname AS \"Result\""); - if (description) - strcat(descbuf, ",\n obj_description(p.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n" + if (desc) + strcat(buf, ",\n obj_description(p.oid) as \"Description\""); + strcat(buf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n" "WHERE RegprocToOid(o.oprcode) = p.oid AND\n" " o.oprresult = t0.oid AND\n" " o.oprkind = 'r' AND\n" " o.oprleft = t1.oid\n"); if (name) { - strcat(descbuf, "AND o.oprname = '"); - strncat(descbuf, name, 32); - strcat(descbuf, "'\n"); + strcat(buf, "AND o.oprname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } - strcat(descbuf, "\nORDER BY \"Op\", \"Left arg\", \"Right arg\", \"Result\""); + strcat(buf, "\nORDER BY \"Op\", \"Left arg\", \"Right arg\", \"Result\""); - res = PSQLexec(pset, descbuf); + res = PSQLexec(pset, buf); if (!res) return false; @@ -283,26 +309,26 @@ describeOperators(const char *name, PsqlSettings *pset) * for \l, \list, and -l switch */ bool -listAllDbs(PsqlSettings *pset) +listAllDbs(PsqlSettings *pset, bool desc) { PGresult *res; - char descbuf[256]; + char buf[256]; printQueryOpt myopt = pset->popt; - descbuf[0] = '\0'; - strcat(descbuf, "SELECT pg_database.datname as \"Database\",\n" + strcpy(buf, + "SELECT pg_database.datname as \"Database\",\n" " pg_user.usename as \"Owner\"" #ifdef MULTIBYTE ",\n pg_database.encoding as \"Encoding\"" #endif ); - if (GetVariableBool(pset->vars, "description")) - strcat(descbuf, ",\n obj_description(pg_database.oid) as \"Description\"\n"); - strcat(descbuf, "FROM pg_database, pg_user\n" + if (desc) + strcat(buf, ",\n obj_description(pg_database.oid) as \"Description\"\n"); + strcat(buf, "FROM pg_database, pg_user\n" "WHERE pg_database.datdba = pg_user.usesysid\n" "ORDER BY \"Database\""); - res = PSQLexec(pset, descbuf); + res = PSQLexec(pset, buf); if (!res) return false; @@ -337,7 +363,7 @@ permissionsList(const char *name, PsqlSettings *pset) " relname !~ '^pg_'\n"); if (name) { - strcat(descbuf, " AND rename ~ '^"); + strcat(descbuf, " AND rename ~ '"); strncat(descbuf, name, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -389,7 +415,7 @@ objectDescription(const char *object, PsqlSettings *pset) "WHERE a.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND a.aggname ~* '^"); + strcat(descbuf, " AND a.aggname ~* '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -401,7 +427,7 @@ objectDescription(const char *object, PsqlSettings *pset) "WHERE p.oid = d.objoid AND (p.pronargs = 0 or oid8types(p.proargtypes) != '')\n"); if (object) { - strcat(descbuf, " AND p.proname ~* '^"); + strcat(descbuf, " AND p.proname ~* '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -410,11 +436,11 @@ objectDescription(const char *object, PsqlSettings *pset) strcat(descbuf, "\nUNION ALL\n\n"); strcat(descbuf, "SELECT DISTINCT o.oprname as \"Name\", 'operator'::text as \"What\", d.description as \"Description\"\n" "FROM pg_operator o, pg_description d\n" - // must get comment via associated function + /* must get comment via associated function */ "WHERE RegprocToOid(o.oprcode) = d.objoid\n"); if (object) { - strcat(descbuf, " AND o.oprname = '"); + strcat(descbuf, " AND o.oprname ~ '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -426,7 +452,7 @@ objectDescription(const char *object, PsqlSettings *pset) "WHERE t.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND t.typname ~* '^"); + strcat(descbuf, " AND t.typname ~* '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -438,7 +464,7 @@ objectDescription(const char *object, PsqlSettings *pset) "WHERE c.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND c.relname ~* '^"); + strcat(descbuf, " AND c.relname ~* '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -450,7 +476,7 @@ objectDescription(const char *object, PsqlSettings *pset) "WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'\n"); if (object) { - strcat(descbuf, " AND r.rulename ~* '^"); + strcat(descbuf, " AND r.rulename ~* '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -462,7 +488,7 @@ objectDescription(const char *object, PsqlSettings *pset) "WHERE t.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND t.tgname ~* '^"); + strcat(descbuf, " AND t.tgname ~* '"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -510,89 +536,122 @@ xmalloc(size_t size) bool -describeTableDetails(const char *name, PsqlSettings *pset) +describeTableDetails(const char *name, PsqlSettings *pset, bool desc) { - char descbuf[512 + NAMEDATALEN]; - PGresult *res = NULL, - *res2 = NULL, - *res3 = NULL; + char buf[512 + 8 * NAMEDATALEN]; + PGresult *res = NULL; printTableOpt myopt = pset->popt.topt; - bool description = GetVariableBool(pset->vars, "description"); int i; - const char *view_def = NULL; + const char *view_def = NULL; const char *headers[5]; char **cells = NULL; char *title = NULL; char **footers = NULL; char **ptr; unsigned int cols; + struct { bool hasindex; char relkind; int16 checks; int16 triggers; bool hasrules; } tableinfo; + bool error = false; - cols = 3 + (description ? 1 : 0); - - headers[0] = "Attribute"; - headers[1] = "Type"; - headers[2] = "Info"; - if (description) - { - headers[3] = "Description"; - headers[4] = NULL; - } - else - headers[3] = NULL; + /* truncate table name */ + if (strlen(name) > NAMEDATALEN) { + char *my_name = xmalloc(NAMEDATALEN+1); + strncpy(my_name, name, NAMEDATALEN); + my_name[NAMEDATALEN] = '\0'; + name = my_name; + } /* Get general table info */ - strcpy(descbuf, "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum"); - if (description) - strcat(descbuf, ", obj_description(a.oid)"); - strcat(descbuf, "\nFROM pg_class c, pg_attribute a, pg_type t\n" - "WHERE c.relname = '"); - strncat(descbuf, name, NAMEDATALEN); - strcat(descbuf, "'\n AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid\n" - "ORDER BY a.attnum"); - - res = PSQLexec(pset, descbuf); - if (!res) - return false; + sprintf(buf, + "SELECT relhasindex, relkind, relchecks, reltriggers, relhasrules\n" + "FROM pg_class WHERE relname='%s'", + name); + res = PSQLexec(pset, buf); + if (!res) + return false; /* Did we get anything? */ if (PQntuples(res) == 0) { if (!GetVariableBool(pset->vars, "quiet")) - fprintf(stdout, "Did not find any class named \"%s\".\n", name); + fprintf(stdout, "Did not find any relation named \"%s\".\n", name); PQclear(res); return false; } - /* Check if table is a view */ - strcpy(descbuf, "SELECT definition FROM pg_views WHERE viewname = '"); - strncat(descbuf, name, NAMEDATALEN); - strcat(descbuf, "'"); - res2 = PSQLexec(pset, descbuf); - if (!res2) - return false; + /* FIXME: check for null pointers here? */ + tableinfo.hasindex = strcmp(PQgetvalue(res,0,0),"t")==0; + tableinfo.relkind = *(PQgetvalue(res,0,1)); + tableinfo.checks = atoi(PQgetvalue(res,0,2)); + tableinfo.triggers = atoi(PQgetvalue(res,0,3)); + tableinfo.hasrules = strcmp(PQgetvalue(res,0,4),"t")==0; + PQclear(res); - if (PQntuples(res2) > 0) - view_def = PQgetvalue(res2, 0, 0); + headers[0] = "Attribute"; + headers[1] = "Type"; + cols = 2; + if (tableinfo.relkind == 'r' || tableinfo.relkind == 's') + { + cols++; + headers[cols-1] = "Extra"; + } - /* Generate table cells to be printed */ - cells = calloc(PQntuples(res) * cols + 1, sizeof(*cells)); - if (!cells) + if (desc) { - perror("calloc"); - exit(EXIT_FAILURE); + cols++; + headers[cols-1] = "Description"; } + headers[cols] = NULL; + + + /* Get column info */ + strcpy(buf, "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum"); + if (desc) + strcat(buf, ", obj_description(a.oid)"); + strcat(buf, "\nFROM pg_class c, pg_attribute a, pg_type t\n" + "WHERE c.relname = '"); + strncat(buf, name, NAMEDATALEN); + strcat(buf, "'\n AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid\n" + "ORDER BY a.attnum"); + + res = PSQLexec(pset, buf); + if (!res) + return false; + + /* Check if table is a view */ + if (tableinfo.hasrules) { + PGresult *result; + sprintf(buf, "SELECT definition FROM pg_views WHERE viewname = '%s'", name); + result = PSQLexec(pset, buf); + if (!result) + { + PQclear(res); + PQclear(result); + return false; + } + + if (PQntuples(result) > 0) + view_def = xstrdup(PQgetvalue(result, 0, 0)); + PQclear(result); + } + + + /* Generate table cells to be printed */ + cells = xmalloc((PQntuples(res) * cols + 1) * sizeof(*cells)); + cells[PQntuples(res) * cols] = NULL; /* end of list */ + for (i = 0; i < PQntuples(res); i++) { int4 attypmod = atoi(PQgetvalue(res, i, 3)); const char *attype = PQgetvalue(res, i, 1); /* Name */ - cells[i * cols + 0] = (char*)PQgetvalue(res, i, 0); /* don't free this afterwards */ - + cells[i * cols + 0] = (char *)PQgetvalue(res, i, 0); /* don't free this afterwards */ + /* Type */ + /* (convert some internal type names to "readable") */ cells[i * cols + 1] = xmalloc(NAMEDATALEN + 16); if (strcmp(attype, "bpchar") == 0) sprintf(cells[i * cols + 1], "char(%d)", attypmod != -1 ? attypmod - VARHDRSZ : 0); @@ -606,85 +665,235 @@ describeTableDetails(const char *name, PsqlSettings *pset) else strcpy(cells[i * cols + 1], attype); - /* Info */ - cells[i * cols + 2] = xmalloc(128 + 128); /* I'm cutting off the - * 'default' string at 128 */ - cells[i * cols + 2][0] = '\0'; - if (strcmp(PQgetvalue(res, i, 4), "t") == 0) - strcat(cells[i * cols + 2], "not null"); - if (strcmp(PQgetvalue(res, i, 5), "t") == 0) - { - /* handle "default" here */ - strcpy(descbuf, "SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c\n" - "WHERE c.relname = '"); - strncat(descbuf, name, NAMEDATALEN); - strcat(descbuf, "' AND c.oid = d.adrelid AND d.adnum = "); - strcat(descbuf, PQgetvalue(res, i, 6)); - - res3 = PSQLexec(pset, descbuf); - if (!res) - return false; - if (cells[i * cols + 2][0]) - strcat(cells[i * cols + 2], " "); - strcat(cells[i * cols + 2], "default "); - strcat(cells[i * cols + 2], PQgetvalue(res3, 0, 0)); - } + + /* Extra: not null and default */ + /* (I'm cutting off the 'default' string at 128) */ + if (tableinfo.relkind == 'r' || tableinfo.relkind == 's') + { + cells[i * cols + 2] = xmalloc(128 + 128); + cells[i * cols + 2][0] = '\0'; + if (strcmp(PQgetvalue(res, i, 4), "t") == 0) + strcat(cells[i * cols + 2], "not null"); + + /* handle "default" here */ + if (strcmp(PQgetvalue(res, i, 5), "t") == 0) + { + PGresult *result; + + sprintf(buf, "SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c\n" + "WHERE c.relname = '%s' AND c.oid = d.adrelid AND d.adnum = %s", + name, PQgetvalue(res, i, 6)); + + result = PSQLexec(pset, buf); + if (!result) + error = true; + else + { + if (cells[i * cols + 2][0]) + strcat(cells[i * cols + 2], " "); + strcat(cells[i * cols + 2], "default "); + strcat(cells[i * cols + 2], PQgetvalue(result, 0, 0)); + PQclear(result); + } + } + } + + if (error) + break; /* Description */ - if (description) - cells[i * cols + 3] = (char*)PQgetvalue(res, i, 7); + if (desc) + cells[i * cols + cols-1] = (char*)PQgetvalue(res, i, 7); } /* Make title */ - title = xmalloc(10 + strlen(name)); - if (view_def) - sprintf(title, "View \"%s\"", name); - else - sprintf(title, "Table \"%s\"", name); + title = xmalloc(20 + strlen(name)); + switch (tableinfo.relkind) { + case 'r': + if (view_def) + sprintf(title, "View \"%s\"", name); + else + sprintf(title, "Table \"%s\"", name); + break; + case 'S': + sprintf(title, "Sequence \"%s\"", name); + break; + case 'i': + sprintf(title, "Index \"%s\"", name); + break; + case 's': + sprintf(title, "System table \"%s\"", name); + break; + default: + sprintf(title, "?%c?", tableinfo.relkind); + } /* Make footers */ - if (view_def) + /* Information about the index */ + if (tableinfo.relkind == 'i') + { + PGresult * result; + + sprintf(buf, "SELECT i.indisunique, i.indisprimary, a.amname\n" + "FROM pg_index i, pg_class c, pg_am a\n" + "WHERE i.indexrelid = c.oid AND c.relname = '%s' AND c.relam = a.oid", + name); + + result = PSQLexec(pset, buf); + if (!result) + error = true; + else + { + footers = xmalloc(2 * sizeof(*footers)); + footers[0] = xmalloc(NAMEDATALEN + 32); + sprintf(footers[0], "%s%s", + strcmp(PQgetvalue(result, 0, 0), "t")==0 ? "unique " : "", + PQgetvalue(result, 0, 2) + ); + if (strcmp(PQgetvalue(result, 0, 1), "t")==0) + strcat(footers[0], " (primary key)"); + footers[1] = NULL; + } + } + /* Information about the view */ + else if (tableinfo.relkind == 'r' && view_def) { footers = xmalloc(2 * sizeof(*footers)); footers[0] = xmalloc(20 + strlen(view_def)); sprintf(footers[0], "View definition: %s", view_def); footers[1] = NULL; } - else + + /* Information about the table */ + else if (tableinfo.relkind == 'r') { - /* display indices */ - strcpy(descbuf, "SELECT c2.relname\n" - "FROM pg_class c, pg_class c2, pg_index i\n" - "WHERE c.relname = '"); - strncat(descbuf, name, NAMEDATALEN); - strcat(descbuf, "' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" - "ORDER BY c2.relname"); - res3 = PSQLexec(pset, descbuf); - if (!res3) - return false; - - if (PQntuples(res3) > 0) - { - footers = xmalloc((PQntuples(res3) + 1) * sizeof(*footers)); - - for (i = 0; i < PQntuples(res3); i++) - { - footers[i] = xmalloc(10 + NAMEDATALEN); - if (PQntuples(res3) == 1) - sprintf(footers[i], "Index: %s", PQgetvalue(res3, i, 0)); - else if (i == 0) - sprintf(footers[i], "Indices: %s", PQgetvalue(res3, i, 0)); - else - sprintf(footers[i], " %s", PQgetvalue(res3, i, 0)); - } - - footers[i] = NULL; + PGresult *result1=NULL, *result2=NULL, *result3=NULL, *result4=NULL; + int index_count=0, constr_count=0, rule_count=0, trigger_count=0; + int count_footers=0; + + /* count indices */ + if (!error && tableinfo.hasindex) + { + sprintf(buf, "SELECT c2.relname\n" + "FROM pg_class c, pg_class c2, pg_index i\n" + "WHERE c.relname = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" + "ORDER BY c2.relname", + name); + result1 = PSQLexec(pset, buf); + if (!result1) + error = true; + else + index_count = PQntuples(result1); + } + + /* count table (and column) constraints */ + if (!error && tableinfo.checks) + { + sprintf(buf, "SELECT rcsrc\n" + "FROM pg_relcheck r, pg_class c\n" + "WHERE c.relname='%s' AND c.oid = r.rcrelid", + name); + result2 = PSQLexec(pset, buf); + if (!result2) + error = true; + else + constr_count = PQntuples(result2); + } + + /* count rules */ + if (!error && tableinfo.hasrules) + { + sprintf(buf, + "SELECT r.rulename\n" + "FROM pg_rewrite r, pg_class c\n" + "WHERE c.relname='%s' AND c.oid = r.ev_class", + name); + result3 = PSQLexec(pset, buf); + if (!result3) + error = true; + else + rule_count = PQntuples(result3); + } + + /* count triggers */ + if (!error && tableinfo.hasrules) + { + sprintf(buf, + "SELECT t.tgname\n" + "FROM pg_trigger t, pg_class c\n" + "WHERE c.relname='%s' AND c.oid = t.tgrelid", + name); + result4 = PSQLexec(pset, buf); + if (!result4) + error = true; + else + trigger_count = PQntuples(result4); + } + + footers = xmalloc((index_count + constr_count + rule_count + trigger_count + 1) * sizeof(*footers)); + + /* print indices */ + for (i = 0; i < index_count; i++) + { + sprintf(buf, "%s %s", + index_count==1 ? "Index:" : (i==0 ? "Indices:" : " "), + PQgetvalue(result1, i, 0) + ); + if (i < index_count-1) + strcat(buf, ","); + + footers[count_footers++] = xstrdup(buf); + } + + /* print contraints */ + for (i = 0; i < constr_count; i++) + { + sprintf(buf, "%s %s", + constr_count==1 ? "Constraint:" : (i==0 ? "Constraints:" : " "), + PQgetvalue(result2, i, 0) + ); + footers[count_footers++] = xstrdup(buf); + } + + /* print rules */ + for (i = 0; i < rule_count; i++) + { + sprintf(buf, "%s %s", + rule_count==1 ? "Rule:" : (i==0 ? "Rules:" : " "), + PQgetvalue(result3, i, 0) + ); + if (i < rule_count-1) + strcat(buf, ","); + + footers[count_footers++] = xstrdup(buf); + } + + /* print triggers */ + for (i = 0; i < trigger_count; i++) + { + sprintf(buf, "%s %s", + trigger_count==1 ? "Trigger:" : (i==0 ? "Triggers:" : " "), + PQgetvalue(result4, i, 0) + ); + if (i < trigger_count-1) + strcat(buf, ","); + + footers[count_footers++] = xstrdup(buf); } - } + /* end of list marker */ + footers[count_footers] = NULL; + + PQclear(result1); + PQclear(result2); + PQclear(result3); + PQclear(result4); + } - myopt.tuples_only = false; - printTable(title, headers, (const char**)cells, (const char**)footers, "llll", &myopt, pset->queryFout); + if (!error) { + myopt.tuples_only = false; + printTable(title, headers, (const char**)cells, (const char**)footers, "llll", &myopt, pset->queryFout); + } /* clean up */ free(title); @@ -692,7 +901,8 @@ describeTableDetails(const char *name, PsqlSettings *pset) for (i = 0; i < PQntuples(res); i++) { free(cells[i * cols + 1]); - free(cells[i * cols + 2]); + if (tableinfo.relkind == 'r' || tableinfo.relkind == 's') + free(cells[i * cols + 2]); } free(cells); @@ -701,10 +911,8 @@ describeTableDetails(const char *name, PsqlSettings *pset) free(footers); PQclear(res); - PQclear(res2); - PQclear(res3); - return true; + return !error; } @@ -719,11 +927,11 @@ describeTableDetails(const char *name, PsqlSettings *pset) * i - indices * v - views * s - sequences - * S - systems tables (~'^pg_') + * S - systems tables (~ '^pg_') * (any order of the above is fine) */ bool -listTables(const char *infotype, const char *name, PsqlSettings *pset) +listTables(const char *infotype, const char *name, PsqlSettings *pset, bool desc) { bool showTables = strchr(infotype, 't') != NULL; bool showIndices = strchr(infotype, 'i') != NULL; @@ -731,120 +939,121 @@ listTables(const char *infotype, const char *name, PsqlSettings *pset) bool showSeq = strchr(infotype, 's') != NULL; bool showSystem = strchr(infotype, 'S') != NULL; - bool description = GetVariableBool(pset->vars, "description"); - - char descbuf[1536 + 4 * REGEXP_CUTOFF]; + char buf[1536 + 4 * REGEXP_CUTOFF]; PGresult *res; printQueryOpt myopt = pset->popt; - descbuf[0] = '\0'; + buf[0] = '\0'; /* tables */ if (showTables) { - strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'table'::text as \"Type\""); - if (description) - strcat(descbuf, ", obj_description(c.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_class c, pg_user u\n" + strcat(buf, "SELECT c.relname as \"Name\", 'table'::text as \"Type\", u.usename as \"Owner\""); + if (desc) + strcat(buf, ", obj_description(c.oid) as \"Description\""); + strcat(buf, "\nFROM pg_class c, pg_user u\n" "WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n" " AND not exists (select 1 from pg_views where viewname = c.relname)\n"); - strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); + strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(descbuf, " AND c.relname ~ '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND c.relname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } } /* views */ if (showViews) { - if (descbuf[0]) - strcat(descbuf, "\nUNION\n\n"); + if (buf[0]) + strcat(buf, "\nUNION\n\n"); - strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'view'::text as \"Type\""); - if (description) - strcat(descbuf, ", obj_description(c.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_class c, pg_user u\n" + strcat(buf, "SELECT c.relname as \"Name\", 'view'::text as \"Type\", u.usename as \"Owner\""); + if (desc) + strcat(buf, ", obj_description(c.oid) as \"Description\""); + strcat(buf, "\nFROM pg_class c, pg_user u\n" "WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n" " AND exists (select 1 from pg_views where viewname = c.relname)\n"); - strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); + strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(descbuf, " AND c.relname ~ '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND c.relname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } } /* indices, sequences */ if (showIndices || showSeq) { - if (descbuf[0]) - strcat(descbuf, "\nUNION\n\n"); - - strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\",\n" - " (CASE WHEN relkind = 'S' THEN 'sequence'::text ELSE 'index'::text END) as \"Type\""); - if (description) - strcat(descbuf, ", obj_description(c.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_class c, pg_user u\n" + if (buf[0]) + strcat(buf, "\nUNION\n\n"); + + strcat(buf, + "SELECT c.relname as \"Name\",\n" + " (CASE WHEN relkind = 'S' THEN 'sequence'::text ELSE 'index'::text END) as \"Type\",\n" + " u.usename as \"Owner\"" + ); + if (desc) + strcat(buf, ", obj_description(c.oid) as \"Description\""); + strcat(buf, "\nFROM pg_class c, pg_user u\n" "WHERE c.relowner = u.usesysid AND relkind in ("); if (showIndices && showSeq) - strcat(descbuf, "'i', 'S'"); + strcat(buf, "'i', 'S'"); else if (showIndices) - strcat(descbuf, "'i'"); + strcat(buf, "'i'"); else - strcat(descbuf, "'S'"); - strcat(descbuf, ")\n"); + strcat(buf, "'S'"); + strcat(buf, ")\n"); /* ignore large-obj indices */ if (showIndices) - strcat(descbuf, " AND (c.relkind != 'i' OR c.relname !~ '^xinx')\n"); + strcat(buf, " AND (c.relkind != 'i' OR c.relname !~ '^xinx')\n"); - strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); + strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(descbuf, " AND c.relname ~ '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND c.relname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } } /* real system catalogue tables */ if (showSystem && showTables) { - if (descbuf[0]) - strcat(descbuf, "\nUNION\n\n"); + if (buf[0]) + strcat(buf, "\nUNION\n\n"); - strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'system'::text as \"Type\""); - if (description) - strcat(descbuf, ", obj_description(c.oid) as \"Description\""); - strcat(descbuf, "\nFROM pg_class c, pg_user u\n" + strcat(buf, "SELECT c.relname as \"Name\", 'system'::text as \"Type\", u.usename as \"Owner\""); + if (desc) + strcat(buf, ", obj_description(c.oid) as \"Description\""); + strcat(buf, "\nFROM pg_class c, pg_user u\n" "WHERE c.relowner = u.usesysid AND c.relkind = 's'\n"); if (name) { - strcat(descbuf, " AND c.relname ~ '^"); - strncat(descbuf, name, REGEXP_CUTOFF); - strcat(descbuf, "'\n"); + strcat(buf, " AND c.relname ~ '"); + strncat(buf, name, REGEXP_CUTOFF); + strcat(buf, "'\n"); } } - strcat(descbuf, "\nORDER BY \"Name\""); + strcat(buf, "\nORDER BY \"Name\""); - res = PSQLexec(pset, descbuf); + res = PSQLexec(pset, buf); if (!res) return false; if (PQntuples(res) == 0) - fprintf(pset->queryFout, "No matching classes found.\n"); + fprintf(pset->queryFout, "No matching relations found.\n"); else { myopt.topt.tuples_only = false; myopt.nullPrint = NULL; - myopt.title = "List of classes"; + myopt.title = "List of relations"; printQuery(res, &myopt, pset->queryFout); } @@ -854,4 +1063,4 @@ listTables(const char *infotype, const char *name, PsqlSettings *pset) } -/* the end */ +/* end of file */ |