diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 97c3a82efe..cd80723027 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,36 +3,27 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.170 2008/05/05 01:21:03 adunstan Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.171 2008/05/12 22:59:58 alvherre Exp $ */ #include "postgres_fe.h" -#include "describe.h" - -#include "dumputils.h" - -#include "common.h" -#include "settings.h" -#include "print.h" -#include "variables.h" #include -#ifdef WIN32 -/* - * mbvalidate() is used in function describeOneTableDetails() to make sure - * all characters of the cells will be printed to the DOS console in a - * correct way - */ +#include "common.h" +#include "describe.h" +#include "dumputils.h" #include "mbprint.h" -#endif +#include "print.h" +#include "settings.h" +#include "variables.h" static bool describeOneTableDetails(const char *schemaname, const char *relationname, const char *oid, bool verbose); -static bool add_tablespace_footer(char relkind, Oid tablespace, char **footers, - int *count, PQExpBufferData buf, bool newline); +static void add_tablespace_footer(printTableContent *const cont, char relkind, + Oid tablespace, const bool newline); static bool listTSParsersVerbose(const char *pattern); static bool describeOneTSParser(const char *oid, const char *nspname, const char *prsname); @@ -789,11 +780,11 @@ describeOneTableDetails(const char *schemaname, PQExpBufferData buf; PGresult *res = NULL; printTableOpt myopt = pset.popt.topt; + printTableContent cont; int i; char *view_def = NULL; - const char *headers[5]; - char **cells = NULL; - char **footers = NULL; + char *headers[4]; + char **modifiers = NULL; char **ptr; PQExpBufferData title; PQExpBufferData tmpbuf; @@ -852,25 +843,6 @@ describeOneTableDetails(const char *schemaname, atooid(PQgetvalue(res, 0, 6)) : 0; PQclear(res); - headers[0] = _("Column"); - headers[1] = _("Type"); - cols = 2; - - if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v') - { - show_modifiers = true; - cols++; - headers[cols - 1] = _("Modifiers"); - } - - if (verbose) - { - cols++; - headers[cols - 1] = _("Description"); - } - - headers[cols] = NULL; - /* Get column info (index requires additional checks) */ printfPQExpBuffer(&buf, "SELECT a.attname,"); appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod)," @@ -893,6 +865,26 @@ describeOneTableDetails(const char *schemaname, goto error_return; numrows = PQntuples(res); + /* Set the number of columns, and their names */ + cols = 2; + headers[0] = "Column"; + headers[1] = "Type"; + + if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v') + { + show_modifiers = true; + headers[cols++] = "Modifiers"; + modifiers = pg_malloc_zero((numrows + 1) * sizeof(*modifiers)); + } + + if (verbose) + headers[cols++] = "Description"; + + printTableInit(&cont, &myopt, title.data, cols, numrows); + + for (i = 0; i < cols; i++) + printTableAddHeader(&cont, headers[i], true, 'l'); + /* Check if table is a view */ if (tableinfo.relkind == 'v') { @@ -910,33 +902,20 @@ describeOneTableDetails(const char *schemaname, } /* Generate table cells to be printed */ - /* note: initialize all cells[] to NULL in case of error exit */ - cells = pg_malloc_zero((numrows * cols + 1) * sizeof(*cells)); - for (i = 0; i < numrows; i++) { - /* Name */ -#ifdef WIN32 - cells[i * cols + 0] = mbvalidate(PQgetvalue(res, i, 0), myopt.encoding); -#else - cells[i * cols + 0] = PQgetvalue(res, i, 0); /* don't free this - * afterwards */ -#endif + /* Column */ + printTableAddCell(&cont, PQgetvalue(res, i, 0), false); /* Type */ -#ifdef WIN32 - cells[i * cols + 1] = mbvalidate(PQgetvalue(res, i, 1), myopt.encoding); -#else - cells[i * cols + 1] = PQgetvalue(res, i, 1); /* don't free this - * either */ -#endif + printTableAddCell(&cont, PQgetvalue(res, i, 1), false); /* Extra: not null and default */ if (show_modifiers) { resetPQExpBuffer(&tmpbuf); if (strcmp(PQgetvalue(res, i, 3), "t") == 0) - appendPQExpBufferStr(&tmpbuf, "not null"); + appendPQExpBufferStr(&tmpbuf, _("not null")); /* handle "default" here */ /* (note: above we cut off the 'default' string at 128) */ @@ -944,24 +923,18 @@ describeOneTableDetails(const char *schemaname, { if (tmpbuf.len > 0) appendPQExpBufferStr(&tmpbuf, " "); - appendPQExpBuffer(&tmpbuf, "default %s", + /* translator: default values of column definitions */ + appendPQExpBuffer(&tmpbuf, _("default %s"), PQgetvalue(res, i, 2)); } -#ifdef WIN32 - cells[i * cols + 2] = pg_strdup(mbvalidate(tmpbuf.data, myopt.encoding)); -#else - cells[i * cols + 2] = pg_strdup(tmpbuf.data); -#endif + modifiers[i] = pg_strdup(tmpbuf.data); + printTableAddCell(&cont, modifiers[i], false); } /* Description */ if (verbose) -#ifdef WIN32 - cells[i * cols + cols - 1] = mbvalidate(PQgetvalue(res, i, 5), myopt.encoding); -#else - cells[i * cols + cols - 1] = PQgetvalue(res, i, 5); -#endif + printTableAddCell(&cont, PQgetvalue(res, i, 5), false); } /* Make title */ @@ -997,7 +970,8 @@ describeOneTableDetails(const char *schemaname, schemaname, relationname); break; default: - printfPQExpBuffer(&title, _("?%c? \"%s.%s\""), + /* untranslated unknown relkind */ + printfPQExpBuffer(&title, "?%c? \"%s.%s\"", tableinfo.relkind, schemaname, relationname); break; } @@ -1033,7 +1007,6 @@ describeOneTableDetails(const char *schemaname, char *indamname = PQgetvalue(result, 0, 4); char *indtable = PQgetvalue(result, 0, 5); char *indpred = PQgetvalue(result, 0, 6); - int count_footers = 0; if (strcmp(indisprimary, "t") == 0) printfPQExpBuffer(&tmpbuf, _("primary key, ")); @@ -1056,11 +1029,9 @@ describeOneTableDetails(const char *schemaname, if (strcmp(indisvalid, "t") != 0) appendPQExpBuffer(&tmpbuf, _(", invalid")); - footers = pg_malloc_zero(4 * sizeof(*footers)); - footers[count_footers++] = pg_strdup(tmpbuf.data); - add_tablespace_footer(tableinfo.relkind, tableinfo.tablespace, - footers, &count_footers, tmpbuf, true); - footers[count_footers] = NULL; + printTableAddFooter(&cont, tmpbuf.data); + add_tablespace_footer(&cont, tableinfo.relkind, + tableinfo.tablespace, true); } @@ -1069,10 +1040,12 @@ describeOneTableDetails(const char *schemaname, else if (view_def) { PGresult *result = NULL; - int rule_count = 0; - int count_footers = 0; - /* count rules other than the view rule */ + /* Footer information about a view */ + printTableAddFooter(&cont, _("View definition:")); + printTableAddFooter(&cont, view_def); + + /* print rules */ if (tableinfo.hasrules) { printfPQExpBuffer(&buf, @@ -1083,60 +1056,32 @@ describeOneTableDetails(const char *schemaname, result = PSQLexec(buf.data, false); if (!result) goto error_return; - else - rule_count = PQntuples(result); - } - /* Footer information about a view */ - footers = pg_malloc_zero((rule_count + 3) * sizeof(*footers)); - footers[count_footers] = pg_malloc(64 + strlen(view_def)); - snprintf(footers[count_footers], 64 + strlen(view_def), - _("View definition:\n%s"), view_def); - count_footers++; - - /* print rules */ - if (rule_count > 0) - { - printfPQExpBuffer(&buf, _("Rules:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < rule_count; i++) + if (PQntuples(result) > 0) { - const char *ruledef; + printTableAddFooter(&cont, _("Rules:")); + for (i = 0; i < PQntuples(result); i++) + { + const char *ruledef; - /* Everything after "CREATE RULE" is echoed verbatim */ - ruledef = PQgetvalue(result, i, 1); - ruledef += 12; + /* Everything after "CREATE RULE" is echoed verbatim */ + ruledef = PQgetvalue(result, i, 1); + ruledef += 12; - printfPQExpBuffer(&buf, " %s", ruledef); - - footers[count_footers++] = pg_strdup(buf.data); + printfPQExpBuffer(&buf, " %s", ruledef); + printTableAddFooter(&cont, buf.data); + } } PQclear(result); } - - footers[count_footers] = NULL; - } else if (tableinfo.relkind == 'r') { /* Footer information about a table */ - PGresult *result1 = NULL, - *result2 = NULL, - *result3 = NULL, - *result4 = NULL, - *result5 = NULL, - *result6 = NULL, - *result7 = NULL; - int check_count = 0, - index_count = 0, - foreignkey_count = 0, - rule_count = 0, - trigger_count = 0, - referencedby_count = 0, - inherits_count = 0; - int count_footers = 0; + PGresult *result = NULL; + int tuples = 0; - /* count indexes */ + /* print indexes */ if (tableinfo.hasindex) { printfPQExpBuffer(&buf, @@ -1146,14 +1091,57 @@ describeOneTableDetails(const char *schemaname, "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", oid); - result1 = PSQLexec(buf.data, false); - if (!result1) + result = PSQLexec(buf.data, false); + if (!result) goto error_return; else - index_count = PQntuples(result1); + tuples = PQntuples(result); + + if (tuples > 0) + { + printTableAddFooter(&cont, _("Indexes:")); + for (i = 0; i < tuples; i++) + { + const char *indexdef; + const char *usingpos; + + /* untranslated index name */ + printfPQExpBuffer(&buf, " \"%s\"", + PQgetvalue(result, i, 0)); + + /* Label as primary key or unique (but not both) */ + appendPQExpBuffer(&buf, + strcmp(PQgetvalue(result, i, 1), "t") == 0 + ? " PRIMARY KEY," : + (strcmp(PQgetvalue(result, i, 2), "t") == 0 + ? " UNIQUE," + : "")); + /* Everything after "USING" is echoed verbatim */ + indexdef = PQgetvalue(result, i, 5); + usingpos = strstr(indexdef, " USING "); + if (usingpos) + indexdef = usingpos + 7; + + appendPQExpBuffer(&buf, " %s", indexdef); + + if (strcmp(PQgetvalue(result, i, 3), "t") == 0) + appendPQExpBuffer(&buf, " CLUSTER"); + + if (strcmp(PQgetvalue(result, i, 4), "t") != 0) + appendPQExpBuffer(&buf, " INVALID"); + + printTableAddFooter(&cont, buf.data); + + /* Print tablespace of the index on the same line */ + add_tablespace_footer(&cont, 'i', + atooid(PQgetvalue(result, i, 6)), + false); + } + } + PQclear(result); } - /* count table (and column) check constraints */ + /* print table (and column) check constraints */ if (tableinfo.checks) { printfPQExpBuffer(&buf, @@ -1162,17 +1150,93 @@ describeOneTableDetails(const char *schemaname, "FROM pg_catalog.pg_constraint r\n" "WHERE r.conrelid = '%s' AND r.contype = 'c' ORDER BY 1", oid); - result2 = PSQLexec(buf.data, false); - if (!result2) - { - PQclear(result1); + result = PSQLexec(buf.data, false); + if (!result) goto error_return; - } else - check_count = PQntuples(result2); + tuples = PQntuples(result); + + if (tuples > 0) + { + printTableAddFooter(&cont, _("Check constraints:")); + for (i = 0; i < tuples; i++) + { + /* untranslated contraint name and def */ + printfPQExpBuffer(&buf, " \"%s\" %s", + PQgetvalue(result, i, 0), + PQgetvalue(result, i, 1)); + + printTableAddFooter(&cont, buf.data); + } + } + PQclear(result); } - /* count rules */ + /* print foreign-key constraints (there are none if no triggers) */ + if (tableinfo.triggers) + { + printfPQExpBuffer(&buf, + "SELECT conname,\n" + " pg_catalog.pg_get_constraintdef(oid, true) as condef\n" + "FROM pg_catalog.pg_constraint r\n" + "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1", + oid); + result = PSQLexec(buf.data, false); + if (!result) + goto error_return; + else + tuples = PQntuples(result); + + if (tuples > 0) + { + printTableAddFooter(&cont, _("Foreign-key constraints:")); + for (i = 0; i < tuples; i++) + { + /* untranslated constraint name and def */ + printfPQExpBuffer(&buf, " \"%s\" %s", + PQgetvalue(result, i, 0), + PQgetvalue(result, i, 1)); + + printTableAddFooter(&cont, buf.data); + } + } + PQclear(result); + } + + /* print incoming foreign-key references (none if no triggers) */ + if (tableinfo.triggers) + { + printfPQExpBuffer(&buf, + "SELECT conname, conrelid::pg_catalog.regclass,\n" + " pg_catalog.pg_get_constraintdef(oid, true) as condef\n" + "FROM pg_catalog.pg_constraint c\n" + "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1", + oid); + result = PSQLexec(buf.data, false); + if (!result) + goto error_return; + else + tuples = PQntuples(result); + + if (tuples > 0) + { + printTableAddFooter(&cont, _("Referenced by:")); + for (i = 0; i < tuples; i++) + { + /* translator: the first %s is a FK name, the following are + * a table name and the FK definition */ + printfPQExpBuffer(&buf, _(" \"%s\" IN %s %s"), + PQgetvalue(result, i, 0), + PQgetvalue(result, i, 1), + PQgetvalue(result, i, 2)); + + printTableAddFooter(&cont, buf.data); + } + } + PQclear(result); + } + + /* print rules */ if (tableinfo.hasrules) { if (pset.sversion < 80300) @@ -1193,18 +1257,81 @@ describeOneTableDetails(const char *schemaname, "WHERE r.ev_class = '%s' ORDER BY 1", oid); } - result3 = PSQLexec(buf.data, false); - if (!result3) - { - PQclear(result1); - PQclear(result2); + result = PSQLexec(buf.data, false); + if (!result) goto error_return; - } else - rule_count = PQntuples(result3); + tuples = PQntuples(result); + + if (tuples > 0) + { + bool have_heading; + int category; + + for (category = 0; category < 4; category++) + { + have_heading = false; + + for (i = 0; i < tuples; i++) + { + const char *ruledef; + bool list_rule = false; + + switch (category) + { + case 0: + if (*PQgetvalue(result, i, 2) == 'O') + list_rule = true; + break; + case 1: + if (*PQgetvalue(result, i, 2) == 'D') + list_rule = true; + break; + case 2: + if (*PQgetvalue(result, i, 2) == 'A') + list_rule = true; + break; + case 3: + if (*PQgetvalue(result, i, 2) == 'R') + list_rule = true; + break; + } + if (!list_rule) + continue; + + if (!have_heading) + { + switch (category) + { + case 0: + printfPQExpBuffer(&buf, _("Rules:")); + break; + case 1: + printfPQExpBuffer(&buf, _("Disabled rules:")); + break; + case 2: + printfPQExpBuffer(&buf, _("Rules firing always:")); + break; + case 3: + printfPQExpBuffer(&buf, _("Rules firing on replica only:")); + break; + } + printTableAddFooter(&cont, buf.data); + have_heading = true; + } + + /* Everything after "CREATE RULE" is echoed verbatim */ + ruledef = PQgetvalue(result, i, 1); + ruledef += 12; + printfPQExpBuffer(&buf, " %s", ruledef); + printTableAddFooter(&cont, buf.data); + } + } + } + PQclear(result); } - /* count triggers (but ignore foreign-key triggers) */ + /* print triggers (but ignore foreign-key triggers) */ if (tableinfo.triggers) { printfPQExpBuffer(&buf, @@ -1215,346 +1342,117 @@ describeOneTableDetails(const char *schemaname, "AND t.tgconstraint = 0\n" "ORDER BY 1", oid); - result4 = PSQLexec(buf.data, false); - if (!result4) - { - PQclear(result1); - PQclear(result2); - PQclear(result3); + result = PSQLexec(buf.data, false); + if (!result) goto error_return; - } else - trigger_count = PQntuples(result4); + tuples = PQntuples(result); + + if (tuples > 0) + { + bool have_heading; + int category; + + /* + * split the output into 4 different categories. Enabled triggers, + * disabled triggers and the two special ALWAYS and REPLICA + * configurations. + */ + for (category = 0; category < 4; category++) + { + have_heading = false; + for (i = 0; i < tuples; i++) + { + bool list_trigger; + const char *tgdef; + const char *usingpos; + const char *tgenabled; + + /* Check if this trigger falls into the current category */ + tgenabled = PQgetvalue(result, i, 2); + list_trigger = false; + switch (category) + { + case 0: + if (*tgenabled == 'O' || *tgenabled == 't') + list_trigger = true; + break; + case 1: + if (*tgenabled == 'D' || *tgenabled == 'f') + list_trigger = true; + break; + case 2: + if (*tgenabled == 'A') + list_trigger = true; + break; + case 3: + if (*tgenabled == 'R') + list_trigger = true; + break; + } + if (list_trigger == false) + continue; + + /* Print the category heading once */ + if (have_heading == false) + { + switch (category) + { + case 0: + printfPQExpBuffer(&buf, _("Triggers:")); + break; + case 1: + printfPQExpBuffer(&buf, _("Disabled triggers:")); + break; + case 2: + printfPQExpBuffer(&buf, _("Triggers firing always:")); + break; + case 3: + printfPQExpBuffer(&buf, _("Triggers firing on replica only:")); + break; + + } + printTableAddFooter(&cont, buf.data); + have_heading = true; + } + + /* Everything after "TRIGGER" is echoed verbatim */ + tgdef = PQgetvalue(result, i, 1); + usingpos = strstr(tgdef, " TRIGGER "); + if (usingpos) + tgdef = usingpos + 9; + + printfPQExpBuffer(&buf, " %s", tgdef); + printTableAddFooter(&cont, buf.data); + } + } + } + PQclear(result); } - /* count foreign-key constraints (there are none if no triggers) */ - if (tableinfo.triggers) - { - printfPQExpBuffer(&buf, - "SELECT conname,\n" - " pg_catalog.pg_get_constraintdef(oid, true) as condef\n" - "FROM pg_catalog.pg_constraint r\n" - "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1", - oid); - result5 = PSQLexec(buf.data, false); - if (!result5) - { - PQclear(result1); - PQclear(result2); - PQclear(result3); - PQclear(result4); - goto error_return; - } - else - foreignkey_count = PQntuples(result5); - } - - /* count incoming foreign-key references (none if no triggers) */ - if (tableinfo.triggers) - { - printfPQExpBuffer(&buf, - "SELECT conname, conrelid::pg_catalog.regclass,\n" - " pg_catalog.pg_get_constraintdef(oid, true) as condef\n" - "FROM pg_catalog.pg_constraint c\n" - "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1", - oid); - result6 = PSQLexec(buf.data, false); - if (!result6) - { - PQclear(result1); - PQclear(result2); - PQclear(result3); - PQclear(result4); - PQclear(result5); - goto error_return; - } - else - referencedby_count = PQntuples(result6); - } - - /* count inherited tables */ + /* print inherited tables */ printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid); - result7 = PSQLexec(buf.data, false); - if (!result7) - { - PQclear(result1); - PQclear(result2); - PQclear(result3); - PQclear(result4); - PQclear(result5); - PQclear(result6); + result = PSQLexec(buf.data, false); + if (!result) goto error_return; - } else - inherits_count = PQntuples(result7); + tuples = PQntuples(result); - footers = pg_malloc_zero((index_count + check_count + rule_count + trigger_count + foreignkey_count + referencedby_count + inherits_count + 8 + 1) * sizeof(*footers)); - - /* print indexes */ - if (index_count > 0) - { - printfPQExpBuffer(&buf, _("Indexes:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < index_count; i++) - { - const char *indexdef; - const char *usingpos; - PQExpBufferData tmpbuf; - - /* Output index name */ - printfPQExpBuffer(&buf, _(" \"%s\""), - PQgetvalue(result1, i, 0)); - - /* Label as primary key or unique (but not both) */ - appendPQExpBuffer(&buf, - strcmp(PQgetvalue(result1, i, 1), "t") == 0 - ? " PRIMARY KEY," : - (strcmp(PQgetvalue(result1, i, 2), "t") == 0 - ? " UNIQUE," - : "")); - /* Everything after "USING" is echoed verbatim */ - indexdef = PQgetvalue(result1, i, 5); - usingpos = strstr(indexdef, " USING "); - if (usingpos) - indexdef = usingpos + 7; - - appendPQExpBuffer(&buf, " %s", indexdef); - - if (strcmp(PQgetvalue(result1, i, 3), "t") == 0) - appendPQExpBuffer(&buf, " CLUSTER"); - - if (strcmp(PQgetvalue(result1, i, 4), "t") != 0) - appendPQExpBuffer(&buf, " INVALID"); - - /* Print tablespace of the index on the same line */ - count_footers += 1; - initPQExpBuffer(&tmpbuf); - if (add_tablespace_footer('i', - atooid(PQgetvalue(result1, i, 6)), - footers, &count_footers, tmpbuf, false)) - { - appendPQExpBuffer(&buf, ", "); - appendPQExpBuffer(&buf, tmpbuf.data); - - count_footers -= 2; - } - else - count_footers -= 1; - termPQExpBuffer(&tmpbuf); - - footers[count_footers++] = pg_strdup(buf.data); - } - } - - /* print check constraints */ - if (check_count > 0) - { - printfPQExpBuffer(&buf, _("Check constraints:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < check_count; i++) - { - printfPQExpBuffer(&buf, _(" \"%s\" %s"), - PQgetvalue(result2, i, 0), - PQgetvalue(result2, i, 1)); - - footers[count_footers++] = pg_strdup(buf.data); - } - } - - /* print foreign key constraints */ - if (foreignkey_count > 0) - { - printfPQExpBuffer(&buf, _("Foreign-key constraints:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < foreignkey_count; i++) - { - printfPQExpBuffer(&buf, _(" \"%s\" %s"), - PQgetvalue(result5, i, 0), - PQgetvalue(result5, i, 1)); - - footers[count_footers++] = pg_strdup(buf.data); - } - } - - /* print incoming foreign-key constraints */ - if (referencedby_count > 0) - { - printfPQExpBuffer(&buf, _("Referenced by:")); - footers[count_footers++] = pg_strdup(buf.data); - for (i = 0; i < referencedby_count; i++) - { - printfPQExpBuffer(&buf, _(" \"%s\" IN %s %s"), - PQgetvalue(result6, i, 0), - PQgetvalue(result6, i, 1), - PQgetvalue(result6, i, 2)); - - footers[count_footers++] = pg_strdup(buf.data); - } - } - - /* print rules */ - if (rule_count > 0) - { - bool have_heading; - int category; - - for (category = 0; category < 4; category++) - { - have_heading = false; - - for (i = 0; i < rule_count; i++) - { - const char *ruledef; - bool list_rule = false; - - switch (category) - { - case 0: - if (*PQgetvalue(result3, i, 2) == 'O') - list_rule = true; - break; - case 1: - if (*PQgetvalue(result3, i, 2) == 'D') - list_rule = true; - break; - case 2: - if (*PQgetvalue(result3, i, 2) == 'A') - list_rule = true; - break; - case 3: - if (*PQgetvalue(result3, i, 2) == 'R') - list_rule = true; - break; - } - if (!list_rule) - continue; - - if (!have_heading) - { - switch (category) - { - case 0: - printfPQExpBuffer(&buf, _("Rules:")); - break; - case 1: - printfPQExpBuffer(&buf, _("Disabled rules:")); - break; - case 2: - printfPQExpBuffer(&buf, _("Rules firing always:")); - break; - case 3: - printfPQExpBuffer(&buf, _("Rules firing on replica only:")); - break; - } - footers[count_footers++] = pg_strdup(buf.data); - have_heading = true; - } - - /* Everything after "CREATE RULE" is echoed verbatim */ - ruledef = PQgetvalue(result3, i, 1); - ruledef += 12; - printfPQExpBuffer(&buf, " %s", ruledef); - footers[count_footers++] = pg_strdup(buf.data); - } - } - } - - /* print triggers */ - if (trigger_count > 0) - { - bool have_heading; - int category; - - /* - * split the output into 4 different categories. Enabled triggers, - * disabled triggers and the two special ALWAYS and REPLICA - * configurations. - */ - for (category = 0; category < 4; category++) - { - have_heading = false; - for (i = 0; i < trigger_count; i++) - { - bool list_trigger; - const char *tgdef; - const char *usingpos; - const char *tgenabled; - - /* Check if this trigger falls into the current category */ - tgenabled = PQgetvalue(result4, i, 2); - list_trigger = false; - switch (category) - { - case 0: - if (*tgenabled == 'O' || *tgenabled == 't') - list_trigger = true; - break; - case 1: - if (*tgenabled == 'D' || *tgenabled == 'f') - list_trigger = true; - break; - case 2: - if (*tgenabled == 'A') - list_trigger = true; - break; - case 3: - if (*tgenabled == 'R') - list_trigger = true; - break; - } - if (list_trigger == false) - continue; - - /* Print the category heading once */ - if (have_heading == false) - { - switch (category) - { - case 0: - printfPQExpBuffer(&buf, _("Triggers:")); - break; - case 1: - printfPQExpBuffer(&buf, _("Disabled triggers:")); - break; - case 2: - printfPQExpBuffer(&buf, _("Triggers firing always:")); - break; - case 3: - printfPQExpBuffer(&buf, _("Triggers firing on replica only:")); - break; - - } - footers[count_footers++] = pg_strdup(buf.data); - have_heading = true; - } - - /* Everything after "TRIGGER" is echoed verbatim */ - tgdef = PQgetvalue(result4, i, 1); - usingpos = strstr(tgdef, " TRIGGER "); - if (usingpos) - tgdef = usingpos + 9; - - printfPQExpBuffer(&buf, " %s", tgdef); - footers[count_footers++] = pg_strdup(buf.data); - } - } - } - - /* print inherits */ - for (i = 0; i < inherits_count; i++) + for (i = 0; i < tuples; i++) { const char *s = _("Inherits"); if (i == 0) - printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result7, i, 0)); + printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result, i, 0)); else - printfPQExpBuffer(&buf, "%*s %s", (int) strlen(s), "", PQgetvalue(result7, i, 0)); - if (i < inherits_count - 1) + printfPQExpBuffer(&buf, "%*s %s", (int) strlen(s), "", PQgetvalue(result, i, 0)); + if (i < tuples - 1) appendPQExpBuffer(&buf, ","); - footers[count_footers++] = pg_strdup(buf.data); + printTableAddFooter(&cont, buf.data); } + PQclear(result); if (verbose) { @@ -1562,51 +1460,31 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "%s: %s", s, (tableinfo.hasoids ? _("yes") : _("no"))); - footers[count_footers++] = pg_strdup(buf.data); + printTableAddFooter(&cont, buf.data); } - add_tablespace_footer(tableinfo.relkind, tableinfo.tablespace, - footers, &count_footers, buf, true); - /* end of list marker */ - footers[count_footers] = NULL; - - PQclear(result1); - PQclear(result2); - PQclear(result3); - PQclear(result4); - PQclear(result5); - PQclear(result6); - PQclear(result7); + add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace, + true); } - printTable(title.data, headers, - (const char **) cells, (const char **) footers, - "llll", &myopt, pset.queryFout, pset.logfile); + printTable(&cont, pset.queryFout, pset.logfile); + printTableCleanup(&cont); retval = true; error_return: /* clean up */ + printTableCleanup(&cont); termPQExpBuffer(&buf); termPQExpBuffer(&title); termPQExpBuffer(&tmpbuf); - if (cells) + if (show_modifiers) { - for (i = 0; i < numrows; i++) - { - if (show_modifiers) - free(cells[i * cols + 2]); - } - free(cells); - } - - if (footers) - { - for (ptr = footers; *ptr; ptr++) + for (ptr = modifiers; *ptr; ptr++) free(*ptr); - free(footers); + free(modifiers); } if (view_def) @@ -1618,14 +1496,14 @@ error_return: return retval; } - /* - * Return true if the relation uses non default tablespace; - * otherwise return false + * Add a tablespace description to a footer. If 'newline' is true, it is added + * in a new line; otherwise it's appended to the current value of the last + * footer. */ -static bool -add_tablespace_footer(char relkind, Oid tablespace, char **footers, - int *count, PQExpBufferData buf, bool newline) +static void +add_tablespace_footer(printTableContent *const cont, char relkind, + Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ if (relkind == 'r' || relkind == 'i') @@ -1636,29 +1514,40 @@ add_tablespace_footer(char relkind, Oid tablespace, char **footers, */ if (tablespace != 0) { - PGresult *result1 = NULL; + PGresult *result = NULL; + PQExpBufferData buf; + initPQExpBuffer(&buf); printfPQExpBuffer(&buf, "SELECT spcname FROM pg_tablespace \n" "WHERE oid = '%u';", tablespace); - result1 = PSQLexec(buf.data, false); - if (!result1) - return false; + result = PSQLexec(buf.data, false); + if (!result) + return; /* Should always be the case, but.... */ - if (PQntuples(result1) > 0) + if (PQntuples(result) > 0) { - printfPQExpBuffer(&buf, - newline ? _("Tablespace: \"%s\"") : _("tablespace \"%s\""), - PQgetvalue(result1, 0, 0)); - - footers[(*count)++] = pg_strdup(buf.data); + if (newline) + { + /* Add the tablespace as a new footer */ + printfPQExpBuffer(&buf, _("Tablespace: \"%s\""), + PQgetvalue(result, 0, 0)); + printTableAddFooter(cont, buf.data); + } + else + { + /* Append the tablespace to the latest footer */ + printfPQExpBuffer(&buf, "%s", cont->footer->data); + /* translator: before this string there's an index + * description like '"foo_pkey" PRIMARY KEY, btree (a)' */ + appendPQExpBuffer(&buf, _(", tablespace \"%s\""), + PQgetvalue(result, 0, 0)); + printTableSetFooter(cont, buf.data); + } } - PQclear(result1); - - return true; + PQclear(result); + termPQExpBuffer(&buf); } } - - return false; } /* diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 68c675c06f..2dcf964a8a 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -3,13 +3,10 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.99 2008/05/10 03:31:58 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.100 2008/05/12 22:59:58 alvherre Exp $ */ #include "postgres_fe.h" -#include "print.h" -#include "catalog/pg_type.h" - #include #include #include @@ -24,9 +21,12 @@ #include +#include "catalog/pg_type.h" #include "pqsignal.h" +#include "common.h" #include "mbprint.h" +#include "print.h" /* * We define the cancel_pressed flag in this file, rather than common.c where @@ -175,16 +175,12 @@ format_numeric_locale(const char *my_str) static void -print_unaligned_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_unaligned_text(const printTableContent *cont, FILE *fout) { - const char *opt_fieldsep = opt->fieldSep; - const char *opt_recordsep = opt->recordSep; - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned int col_count = 0; + const char *opt_fieldsep = cont->opt->fieldSep; + const char *opt_recordsep = cont->opt->recordSep; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; unsigned int i; const char *const * ptr; bool need_recordsep = false; @@ -197,22 +193,18 @@ print_unaligned_text(const char *title, const char *const * headers, if (!opt_recordsep) opt_recordsep = ""; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) - fprintf(fout, "%s%s", title, opt_recordsep); + if (!opt_tuples_only && cont->title) + fprintf(fout, "%s%s", cont->title, opt_recordsep); /* print headers */ if (!opt_tuples_only) { - for (ptr = headers; *ptr; ptr++) + for (ptr = cont->headers; *ptr; ptr++) { - if (ptr != headers) + if (ptr != cont->headers) fputs(opt_fieldsep, fout); fputs(*ptr, fout); } @@ -224,7 +216,7 @@ print_unaligned_text(const char *title, const char *const * headers, need_recordsep = true; /* print cells */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { if (need_recordsep) { @@ -233,7 +225,7 @@ print_unaligned_text(const char *title, const char *const * headers, if (cancel_pressed) break; } - if (opt_align[i % col_count] == 'r' && opt_numeric_locale) + if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale) { char *my_cell = format_numeric_locale(*ptr); @@ -243,27 +235,30 @@ print_unaligned_text(const char *title, const char *const * headers, else fputs(*ptr, fout); - if ((i + 1) % col_count) + if ((i + 1) % cont->ncolumns) fputs(opt_fieldsep, fout); else need_recordsep = true; } /* print footers */ - if (opt->stop_table) + if (cont->opt->stop_table) { - if (!opt_tuples_only && footers && !cancel_pressed) - for (ptr = footers; *ptr; ptr++) + if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) + { + printTableFooter *f; + + for (f = cont->footers; f; f = f->next) { if (need_recordsep) { fputs(opt_recordsep, fout); need_recordsep = false; } - fputs(*ptr, fout); + fputs(f->data, fout); need_recordsep = true; } - + } /* the last record needs to be concluded with a newline */ if (need_recordsep) fputc('\n', fout); @@ -272,16 +267,12 @@ print_unaligned_text(const char *title, const char *const * headers, static void -print_unaligned_vertical(const char *title, const char *const * headers, - const char *const * cells, - const char *const * footers, const char *opt_align, - const printTableOpt *opt, FILE *fout) +print_unaligned_vertical(const printTableContent *cont, FILE *fout) { - const char *opt_fieldsep = opt->fieldSep; - const char *opt_recordsep = opt->recordSep; - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned int col_count = 0; + const char *opt_fieldsep = cont->opt->fieldSep; + const char *opt_recordsep = cont->opt->recordSep; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; unsigned int i; const char *const * ptr; bool need_recordsep = false; @@ -294,16 +285,12 @@ print_unaligned_vertical(const char *title, const char *const * headers, if (!opt_recordsep) opt_recordsep = ""; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { - fputs(title, fout); + fputs(cont->title, fout); need_recordsep = true; } } @@ -312,7 +299,7 @@ print_unaligned_vertical(const char *title, const char *const * headers, need_recordsep = true; /* print records */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { if (need_recordsep) { @@ -324,9 +311,9 @@ print_unaligned_vertical(const char *title, const char *const * headers, break; } - fputs(headers[i % col_count], fout); + fputs(cont->headers[i % cont->ncolumns], fout); fputs(opt_fieldsep, fout); - if (opt_align[i % col_count] == 'r' && opt_numeric_locale) + if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale) { char *my_cell = format_numeric_locale(*ptr); @@ -336,22 +323,24 @@ print_unaligned_vertical(const char *title, const char *const * headers, else fputs(*ptr, fout); - if ((i + 1) % col_count) + if ((i + 1) % cont->ncolumns) fputs(opt_recordsep, fout); else need_recordsep = true; } - if (opt->stop_table) + if (cont->opt->stop_table) { /* print footers */ - if (!opt_tuples_only && footers && *footers && !cancel_pressed) + if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) { + printTableFooter *f; + fputs(opt_recordsep, fout); - for (ptr = footers; *ptr; ptr++) + for (f = cont->footers; f; f = f->next) { fputs(opt_recordsep, fout); - fputs(*ptr, fout); + fputs(f->data, fout); } } @@ -367,7 +356,8 @@ print_unaligned_vertical(const char *title, const char *const * headers, /* draw "line" */ static void -_print_horizontal_line(const unsigned int col_count, const unsigned int *widths, unsigned short border, FILE *fout) +_print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths, + unsigned short border, FILE *fout) { unsigned int i, j; @@ -377,12 +367,12 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, else if (border == 2) fputs("+-", fout); - for (i = 0; i < col_count; i++) + for (i = 0; i < ncolumns; i++) { for (j = 0; j < widths[i]; j++) fputc('-', fout); - if (i < col_count - 1) + if (i < ncolumns - 1) { if (border == 0) fputc(' ', fout); @@ -404,15 +394,12 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, * Print pretty boxes around cells. */ static void -print_aligned_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - bool is_pager, FILE *fout) +print_aligned_text(const printTableContent *cont, bool is_pager, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - int encoding = opt->encoding; - unsigned short opt_border = opt->border; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + int encoding = cont->opt->encoding; + unsigned short opt_border = cont->opt->border; unsigned int col_count = 0, cell_count = 0; @@ -444,12 +431,9 @@ print_aligned_text(const char *title, const char *const * headers, if (opt_border > 2) opt_border = 2; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (col_count > 0) + if (cont->ncolumns > 0) { + col_count = cont->ncolumns; width_header = pg_local_calloc(col_count, sizeof(*width_header)); width_average = pg_local_calloc(col_count, sizeof(*width_average)); max_width = pg_local_calloc(col_count, sizeof(*max_width)); @@ -484,8 +468,8 @@ print_aligned_text(const char *title, const char *const * headers, nl_lines, bytes_required; - pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, - &width, &nl_lines, &bytes_required); + pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]), + encoding, &width, &nl_lines, &bytes_required); if (width > max_width[i]) max_width[i] = width; if (nl_lines > max_nl_lines[i]) @@ -497,7 +481,7 @@ print_aligned_text(const char *title, const char *const * headers, } /* scan all cells, find maximum width, compute cell_count */ - for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++) + for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++) { int width, nl_lines, @@ -506,7 +490,7 @@ print_aligned_text(const char *title, const char *const * headers, /* Get width, ignore nl_lines */ pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &nl_lines, &bytes_required); - if (opt_numeric_locale && opt_align[i % col_count] == 'r') + if (opt_numeric_locale && cont->aligns[i % col_count] == 'r') { width += additional_numeric_locale_len(*ptr); bytes_required += additional_numeric_locale_len(*ptr); @@ -568,17 +552,17 @@ print_aligned_text(const char *title, const char *const * headers, for (i = 0; i < col_count; i++) width_wrap[i] = max_width[i]; - if (opt->format == PRINT_WRAPPED) + if (cont->opt->format == PRINT_WRAPPED) { /* * Choose target output width: \pset columns, or $COLUMNS, or ioctl */ - if (opt->columns > 0) - output_columns = opt->columns; + if (cont->opt->columns > 0) + output_columns = cont->opt->columns; else if ((fout == stdout && isatty(fileno(stdout))) || is_pager) { - if (opt->env_columns > 0) - output_columns = opt->env_columns; + if (cont->opt->env_columns > 0) + output_columns = cont->opt->env_columns; #ifdef TIOCGWINSZ else { @@ -640,19 +624,22 @@ print_aligned_text(const char *title, const char *const * headers, } /* time to output */ - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (title && !opt_tuples_only) + if (cont->title && !opt_tuples_only) { int width, height; - pg_wcssize((unsigned char *) title, strlen(title), encoding, - &width, &height, NULL); + pg_wcssize((unsigned char *) cont->title, strlen(cont->title), + encoding, &width, &height, NULL); if (width >= width_total) - fprintf(fout, "%s\n", title); /* Aligned */ + /* Aligned */ + fprintf(fout, "%s\n", cont->title); else - fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */ + /* Centered */ + fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", + cont->title); } /* print headers */ @@ -665,8 +652,9 @@ print_aligned_text(const char *title, const char *const * headers, _print_horizontal_line(col_count, width_wrap, opt_border, fout); for (i = 0; i < col_count; i++) - pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), - encoding, col_lineptrs[i], max_nl_lines[i]); + pg_wcsformat((unsigned char *) cont->headers[i], + strlen(cont->headers[i]), encoding, + col_lineptrs[i], max_nl_lines[i]); more_col_wrapping = col_count; curr_nl_line = 0; @@ -678,7 +666,7 @@ print_aligned_text(const char *title, const char *const * headers, else if (opt_border == 1) fputc(curr_nl_line ? '+' : ' ', fout); - for (i = 0; i < col_count; i++) + for (i = 0; i < cont->ncolumns; i++) { unsigned int nbspace; @@ -722,7 +710,7 @@ print_aligned_text(const char *title, const char *const * headers, } /* print cells, one loop per row */ - for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count) + for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count) { bool more_lines; @@ -739,7 +727,7 @@ print_aligned_text(const char *title, const char *const * headers, col_lineptrs[j], max_nl_lines[j]); curr_nl_line[j] = 0; - if (opt_numeric_locale && opt_align[j % col_count] == 'r') + if (opt_numeric_locale && cont->aligns[j % col_count] == 'r') { char *my_cell; @@ -798,7 +786,7 @@ print_aligned_text(const char *title, const char *const * headers, if (chars_to_output > width_wrap[j]) chars_to_output = width_wrap[j]; - if (opt_align[j] == 'r') /* Right aligned cell */ + if (cont->aligns[j] == 'r') /* Right aligned cell */ { /* spaces first */ fprintf(fout, "%*s", width_wrap[j] - chars_to_output, ""); @@ -857,15 +845,19 @@ print_aligned_text(const char *title, const char *const * headers, } while (more_lines); } - if (opt->stop_table) + if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) _print_horizontal_line(col_count, width_wrap, opt_border, fout); /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) - for (ptr = footers; *ptr; ptr++) - fprintf(fout, "%s\n", *ptr); + if (cont->footers && !opt_tuples_only && !cancel_pressed) + { + printTableFooter *f; + + for (f = cont->footers; f; f = f->next) + fprintf(fout, "%s\n", f->data); + } /* * for some reason MinGW (and MSVC) outputs an extra newline, so this @@ -894,17 +886,13 @@ print_aligned_text(const char *title, const char *const * headers, static void -print_aligned_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_aligned_vertical(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - int encoding = opt->encoding; - unsigned int col_count = 0; - unsigned long record = opt->prior_records + 1; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; + int encoding = cont->opt->encoding; + unsigned long record = cont->opt->prior_records + 1; const char *const * ptr; unsigned int i, hwidth = 0, @@ -914,7 +902,6 @@ print_aligned_vertical(const char *title, const char *const * headers, hformatsize = 0, dformatsize = 0; char *divider; - unsigned int cell_count = 0; struct lineptr *hlineptr, *dlineptr; @@ -924,25 +911,22 @@ print_aligned_vertical(const char *title, const char *const * headers, if (opt_border > 2) opt_border = 2; - if (cells[0] == NULL && opt->start_table && opt->stop_table) + if (cont->cells[0] == NULL && cont->opt->start_table && + cont->opt->stop_table) { fprintf(fout, _("(No rows)\n")); return; } - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - /* Find the maximum dimensions for the headers */ - for (i = 0; i < col_count; i++) + for (i = 0; i < cont->ncolumns; i++) { int width, height, fs; - pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, - &width, &height, &fs); + pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]), + encoding, &width, &height, &fs); if (width > hwidth) hwidth = width; if (height > hheight) @@ -951,19 +935,15 @@ print_aligned_vertical(const char *title, const char *const * headers, hformatsize = fs; } - /* Count cells, find their lengths */ - for (ptr = cells; *ptr; ptr++) - cell_count++; - /* find longest data cell */ - for (i = 0, ptr = cells; *ptr; ptr++, i++) + for (i = 0, ptr = cont->cells; *ptr; ptr++, i++) { int numeric_locale_len; int width, height, fs; - if (opt_align[i % col_count] == 'r' && opt_numeric_locale) + if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale) numeric_locale_len = additional_numeric_locale_len(*ptr); else numeric_locale_len = 0; @@ -1005,21 +985,21 @@ print_aligned_vertical(const char *title, const char *const * headers, if (opt_border == 2) strcat(divider, "-+"); - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) - fprintf(fout, "%s\n", title); + if (!opt_tuples_only && cont->title) + fprintf(fout, "%s\n", cont->title); } /* print records */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { int line_count, dcomplete, hcomplete; - if (i % col_count == 0) + if (i % cont->ncolumns == 0) { if (cancel_pressed) break; @@ -1038,26 +1018,20 @@ print_aligned_vertical(const char *title, const char *const * headers, fprintf(fout, "%.*s%s\n", opt_border, divider, record_str); else { - char *div_copy = strdup(divider); - - if (!div_copy) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } + char *div_copy = pg_strdup(divider); strncpy(div_copy + opt_border, record_str, record_str_len); fprintf(fout, "%s\n", div_copy); free(div_copy); } } - else if (i != 0 || !opt->start_table || opt_border == 2) + else if (i != 0 || !cont->opt->start_table || opt_border == 2) fprintf(fout, "%s\n", divider); } /* Format the header */ - pg_wcsformat((unsigned char *) headers[i % col_count], - strlen(headers[i % col_count]), + pg_wcsformat((unsigned char *) cont->headers[i % cont->ncolumns], + strlen(cont->headers[i % cont->ncolumns]), encoding, hlineptr, hheight); /* Format the data */ pg_wcsformat((unsigned char *) *ptr, strlen(*ptr), encoding, @@ -1087,7 +1061,7 @@ print_aligned_vertical(const char *title, const char *const * headers, if (!dcomplete) { - if (opt_align[i % col_count] == 'r' && opt_numeric_locale) + if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale) { char *my_cell = format_numeric_locale((char *) dlineptr[line_count].ptr); @@ -1121,18 +1095,20 @@ print_aligned_vertical(const char *title, const char *const * headers, } } - if (opt->stop_table) + if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) fprintf(fout, "%s\n", divider); /* print footers */ - if (!opt_tuples_only && footers && *footers && !cancel_pressed) + if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) { + printTableFooter *f; + if (opt_border < 2) fputc('\n', fout); - for (ptr = footers; *ptr; ptr++) - fprintf(fout, "%s\n", *ptr); + for (f = cont->footers; f; f = f->next) + fprintf(fout, "%s\n", f->data); } fputc('\n', fout); @@ -1193,27 +1169,19 @@ html_escaped_print(const char *in, FILE *fout) static void -print_html_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_html_text(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - const char *opt_table_attr = opt->tableAttr; - unsigned int col_count = 0; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; + const char *opt_table_attr = cont->opt->tableAttr; unsigned int i; const char *const * ptr; if (cancel_pressed) return; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { fprintf(fout, "\n", fout); /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { fputs(" \n", fout); } @@ -1232,7 +1200,7 @@ print_html_text(const char *title, const char *const * headers, if (!opt_tuples_only) { fputs(" \n", fout); - for (ptr = headers; *ptr; ptr++) + for (ptr = cont->headers; *ptr; ptr++) { fputs(" \n", fout); } - fprintf(fout, " \n", fout); - if ((i + 1) % col_count == 0) + if ((i + 1) % cont->ncolumns == 0) fputs(" \n", fout); } - if (opt->stop_table) + if (cont->opt->stop_table) { fputs("
", fout); - html_escaped_print(title, fout); + html_escaped_print(cont->title, fout); fputs("
", fout); html_escaped_print(*ptr, fout); @@ -1243,20 +1211,20 @@ print_html_text(const char *title, const char *const * headers, } /* print cells */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { - if (i % col_count == 0) + if (i % cont->ncolumns == 0) { if (cancel_pressed) break; fputs("
", opt_align[(i) % col_count] == 'r' ? "right" : "left"); + fprintf(fout, " ", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left"); /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs("  ", fout); - else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) + else if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale) { char *my_cell = format_numeric_locale(*ptr); @@ -1268,21 +1236,23 @@ print_html_text(const char *title, const char *const * headers, fputs("
\n", fout); /* print footers */ - if (!opt_tuples_only && footers && *footers && !cancel_pressed) + if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) { + printTableFooter *f; + fputs("

", fout); - for (ptr = footers; *ptr; ptr++) + for (f = cont->footers; f; f = f->next) { - html_escaped_print(*ptr, fout); + html_escaped_print(f->data, fout); fputs("
\n", fout); } fputs("

", fout); @@ -1294,28 +1264,20 @@ print_html_text(const char *title, const char *const * headers, static void -print_html_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_html_vertical(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - const char *opt_table_attr = opt->tableAttr; - unsigned int col_count = 0; - unsigned long record = opt->prior_records + 1; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; + const char *opt_table_attr = cont->opt->tableAttr; + unsigned long record = cont->opt->prior_records + 1; unsigned int i; const char *const * ptr; if (cancel_pressed) return; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { fprintf(fout, "\n", fout); /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { fputs(" \n", fout); } } /* print records */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { - if (i % col_count == 0) + if (i % cont->ncolumns == 0) { if (cancel_pressed) break; @@ -1347,14 +1309,14 @@ print_html_vertical(const char *title, const char *const * headers, } fputs(" \n" " \n", fout); - fprintf(fout, " \n \n", fout); } - if (opt->stop_table) + if (cont->opt->stop_table) { fputs("
", fout); - html_escaped_print(title, fout); + html_escaped_print(cont->title, fout); fputs("
", fout); - html_escaped_print(headers[i % col_count], fout); + html_escaped_print(cont->headers[i % cont->ncolumns], fout); fputs("", opt_align[i % col_count] == 'r' ? "right" : "left"); + fprintf(fout, " ", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left"); /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs("  ", fout); - else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) + else if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale) { char *my_cell = format_numeric_locale(*ptr); @@ -1367,17 +1329,19 @@ print_html_vertical(const char *title, const char *const * headers, fputs("
\n", fout); /* print footers */ - if (!opt_tuples_only && footers && *footers && !cancel_pressed) + if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) { + printTableFooter *f; + fputs("

", fout); - for (ptr = footers; *ptr; ptr++) + for (f = cont->footers; f; f = f->next) { - html_escaped_print(*ptr, fout); + html_escaped_print(f->data, fout); fputs("
\n", fout); } fputs("

", fout); @@ -1432,15 +1396,11 @@ latex_escaped_print(const char *in, FILE *fout) static void -print_latex_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_latex_text(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - unsigned int col_count = 0; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; unsigned int i; const char *const * ptr; @@ -1450,17 +1410,13 @@ print_latex_text(const char *title, const char *const * headers, if (opt_border > 2) opt_border = 2; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { fputs("\\begin{center}\n", fout); - latex_escaped_print(title, fout); + latex_escaped_print(cont->title, fout); fputs("\n\\end{center}\n\n", fout); } @@ -1469,10 +1425,10 @@ print_latex_text(const char *title, const char *const * headers, if (opt_border == 2) fputs("| ", fout); - for (i = 0; i < col_count; i++) + for (i = 0; i < cont->ncolumns; i++) { - fputc(*(opt_align + i), fout); - if (opt_border != 0 && i < col_count - 1) + fputc(*(cont->aligns + i), fout); + if (opt_border != 0 && i < cont->ncolumns - 1) fputs(" | ", fout); } if (opt_border == 2) @@ -1486,7 +1442,7 @@ print_latex_text(const char *title, const char *const * headers, /* print headers */ if (!opt_tuples_only) { - for (i = 0, ptr = headers; i < col_count; i++, ptr++) + for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++) { if (i != 0) fputs(" & ", fout); @@ -1500,7 +1456,7 @@ print_latex_text(const char *title, const char *const * headers, } /* print cells */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { if (opt_numeric_locale) { @@ -1512,7 +1468,7 @@ print_latex_text(const char *title, const char *const * headers, else latex_escaped_print(*ptr, fout); - if ((i + 1) % col_count == 0) + if ((i + 1) % cont->ncolumns == 0) { fputs(" \\\\\n", fout); if (cancel_pressed) @@ -1522,7 +1478,7 @@ print_latex_text(const char *title, const char *const * headers, fputs(" & ", fout); } - if (opt->stop_table) + if (cont->opt->stop_table) { if (opt_border == 2) fputs("\\hline\n", fout); @@ -1530,11 +1486,13 @@ print_latex_text(const char *title, const char *const * headers, fputs("\\end{tabular}\n\n\\noindent ", fout); /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) + if (cont->footers && !opt_tuples_only && !cancel_pressed) { - for (ptr = footers; *ptr; ptr++) + printTableFooter *f; + + for (f = cont->footers; f; f = f->next) { - latex_escaped_print(*ptr, fout); + latex_escaped_print(f->data, fout); fputs(" \\\\\n", fout); } } @@ -1545,38 +1503,28 @@ print_latex_text(const char *title, const char *const * headers, static void -print_latex_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_latex_vertical(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - unsigned int col_count = 0; - unsigned long record = opt->prior_records + 1; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; + unsigned long record = cont->opt->prior_records + 1; unsigned int i; const char *const * ptr; - (void) opt_align; /* currently unused parameter */ - if (cancel_pressed) return; if (opt_border > 2) opt_border = 2; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { fputs("\\begin{center}\n", fout); - latex_escaped_print(title, fout); + latex_escaped_print(cont->title, fout); fputs("\n\\end{center}\n\n", fout); } @@ -1592,10 +1540,10 @@ print_latex_vertical(const char *title, const char *const * headers, } /* print records */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { /* new record */ - if (i % col_count == 0) + if (i % cont->ncolumns == 0) { if (cancel_pressed) break; @@ -1613,13 +1561,13 @@ print_latex_vertical(const char *title, const char *const * headers, fputs("\\hline\n", fout); } - latex_escaped_print(headers[i % col_count], fout); + latex_escaped_print(cont->headers[i % cont->ncolumns], fout); fputs(" & ", fout); latex_escaped_print(*ptr, fout); fputs(" \\\\\n", fout); } - if (opt->stop_table) + if (cont->opt->stop_table) { if (opt_border == 2) fputs("\\hline\n", fout); @@ -1627,19 +1575,21 @@ print_latex_vertical(const char *title, const char *const * headers, fputs("\\end{tabular}\n\n\\noindent ", fout); /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) + if (cont->footers && !opt_tuples_only && !cancel_pressed) { - for (ptr = footers; *ptr; ptr++) + printTableFooter *f; + + for (f = cont->footers; f; f = f->next) { if (opt_numeric_locale) { - char *my_cell = format_numeric_locale(*ptr); + char *my_cell = format_numeric_locale(f->data); latex_escaped_print(my_cell, fout); free(my_cell); } else - latex_escaped_print(*ptr, fout); + latex_escaped_print(f->data, fout); fputs(" \\\\\n", fout); } } @@ -1672,15 +1622,11 @@ troff_ms_escaped_print(const char *in, FILE *fout) static void -print_troff_ms_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_troff_ms_text(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - unsigned int col_count = 0; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; unsigned int i; const char *const * ptr; @@ -1690,17 +1636,13 @@ print_troff_ms_text(const char *title, const char *const * headers, if (opt_border > 2) opt_border = 2; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { fputs(".LP\n.DS C\n", fout); - troff_ms_escaped_print(title, fout); + troff_ms_escaped_print(cont->title, fout); fputs("\n.DE\n", fout); } @@ -1711,10 +1653,10 @@ print_troff_ms_text(const char *title, const char *const * headers, else fputs("center;\n", fout); - for (i = 0; i < col_count; i++) + for (i = 0; i < cont->ncolumns; i++) { - fputc(*(opt_align + i), fout); - if (opt_border > 0 && i < col_count - 1) + fputc(*(cont->aligns + i), fout); + if (opt_border > 0 && i < cont->ncolumns - 1) fputs(" | ", fout); } fputs(".\n", fout); @@ -1722,7 +1664,7 @@ print_troff_ms_text(const char *title, const char *const * headers, /* print headers */ if (!opt_tuples_only) { - for (i = 0, ptr = headers; i < col_count; i++, ptr++) + for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++) { if (i != 0) fputc('\t', fout); @@ -1735,7 +1677,7 @@ print_troff_ms_text(const char *title, const char *const * headers, } /* print cells */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { if (opt_numeric_locale) { @@ -1747,7 +1689,7 @@ print_troff_ms_text(const char *title, const char *const * headers, else troff_ms_escaped_print(*ptr, fout); - if ((i + 1) % col_count == 0) + if ((i + 1) % cont->ncolumns == 0) { fputc('\n', fout); if (cancel_pressed) @@ -1757,17 +1699,21 @@ print_troff_ms_text(const char *title, const char *const * headers, fputc('\t', fout); } - if (opt->stop_table) + if (cont->opt->stop_table) { fputs(".TE\n.DS L\n", fout); /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) - for (ptr = footers; *ptr; ptr++) + if (cont->footers && !opt_tuples_only && !cancel_pressed) + { + printTableFooter *f; + + for (f = cont->footers; f; f = f->next) { - troff_ms_escaped_print(*ptr, fout); + troff_ms_escaped_print(f->data, fout); fputc('\n', fout); } + } fputs(".DE\n", fout); } @@ -1775,39 +1721,29 @@ print_troff_ms_text(const char *title, const char *const * headers, static void -print_troff_ms_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_align, const printTableOpt *opt, - FILE *fout) +print_troff_ms_vertical(const printTableContent *cont, FILE *fout) { - bool opt_tuples_only = opt->tuples_only; - bool opt_numeric_locale = opt->numericLocale; - unsigned short opt_border = opt->border; - unsigned int col_count = 0; - unsigned long record = opt->prior_records + 1; + bool opt_tuples_only = cont->opt->tuples_only; + bool opt_numeric_locale = cont->opt->numericLocale; + unsigned short opt_border = cont->opt->border; + unsigned long record = cont->opt->prior_records + 1; unsigned int i; const char *const * ptr; unsigned short current_format = 0; /* 0=none, 1=header, 2=body */ - (void) opt_align; /* currently unused parameter */ - if (cancel_pressed) return; if (opt_border > 2) opt_border = 2; - /* count columns */ - for (ptr = headers; *ptr; ptr++) - col_count++; - - if (opt->start_table) + if (cont->opt->start_table) { /* print title */ - if (!opt_tuples_only && title) + if (!opt_tuples_only && cont->title) { fputs(".LP\n.DS C\n", fout); - troff_ms_escaped_print(title, fout); + troff_ms_escaped_print(cont->title, fout); fputs("\n.DE\n", fout); } @@ -1826,10 +1762,10 @@ print_troff_ms_vertical(const char *title, const char *const * headers, current_format = 2; /* assume tuples printed already */ /* print records */ - for (i = 0, ptr = cells; *ptr; i++, ptr++) + for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { /* new record */ - if (i % col_count == 0) + if (i % cont->ncolumns == 0) { if (cancel_pressed) break; @@ -1864,7 +1800,7 @@ print_troff_ms_vertical(const char *title, const char *const * headers, } } - troff_ms_escaped_print(headers[i % col_count], fout); + troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout); fputc('\t', fout); if (opt_numeric_locale) { @@ -1879,17 +1815,21 @@ print_troff_ms_vertical(const char *title, const char *const * headers, fputc('\n', fout); } - if (opt->stop_table) + if (cont->opt->stop_table) { fputs(".TE\n.DS L\n", fout); /* print footers */ - if (footers && !opt_tuples_only && !cancel_pressed) - for (ptr = footers; *ptr; ptr++) + if (cont->footers && !opt_tuples_only && !cancel_pressed) + { + printTableFooter *f; + + for (f = cont->footers; f; f = f->next) { - troff_ms_escaped_print(*ptr, fout); + troff_ms_escaped_print(f->data, fout); fputc('\n', fout); } + } fputs(".DE\n", fout); } @@ -1976,53 +1916,239 @@ ClosePager(FILE *pagerpipe) } } - +/* + * Initialise a table contents struct. + * + * Must be called before any other printTable method is used. + * + * If you call this, you must call printTableCleanup once you're done with the + * table. + */ void -printTable(const char *title, - const char *const * headers, - const char *const * cells, - const char *const * footers, - const char *align, - const printTableOpt *opt, FILE *fout, FILE *flog) +printTableInit(printTableContent *const content, const printTableOpt *opt, + const char *title, int ncolumns, int nrows) +{ + content->opt = opt; + content->title = title; + content->ncolumns = ncolumns; + content->nrows = nrows; + + content->headers = pg_local_calloc(ncolumns + 1, + sizeof(*content->headers)); + + content->cells = pg_local_calloc(ncolumns * nrows + 1, + sizeof(*content->cells)); + + content->footers = NULL; + + content->aligns = pg_local_calloc(ncolumns + 1, + sizeof(*content->align)); + + content->header = content->headers; + content->cell = content->cells; + content->footer = content->footers; + content->align = content->aligns; +} + +/* + * Add a header to the table. + * + * Headers are not duplicated; you must ensure that the header string is + * available for the lifetime of the printTableContent struct. + * + * If translate is true, the function will pass the header through gettext. + * Otherwise, the header will not be translated. + * + * align is either 'l' or 'r', and specifies the alignment for cells in this + * column. + */ +void +printTableAddHeader(printTableContent *const content, const char *header, + bool translate, char align) +{ +#ifndef ENABLE_NLS + (void) translate; /* unused parameter */ +#endif + + if (content->header >= content->headers + content->ncolumns) + { + fprintf(stderr, _("Cannot add header to table content: " + "column count of %d exceeded.\n"), + content->ncolumns); + exit(EXIT_FAILURE); + } + + *content->header = (char *) mbvalidate((unsigned char *) header, + content->opt->encoding); +#ifdef ENABLE_NLS + if (translate) + *content->header = _(*content->header); +#endif + content->header++; + + *content->align = align; + content->align++; +} + +/* + * Add a cell to the table. + * + * Cells are not duplicated; you must ensure that the cell string is available + * for the lifetime of the printTableContent struct. + * + * If translate is true, the function will pass the cell through gettext. + * Otherwise, the cell will not be translated. + */ +void +printTableAddCell(printTableContent *const content, const char *cell, + bool translate) +{ +#ifndef ENABLE_NLS + (void) translate; /* unused parameter */ +#endif + + if (content->cell >= content->cells + (content->ncolumns * content->nrows)) + { + fprintf(stderr, _("Cannot add cell to table content: " + "total cell count of %d exceeded.\n"), + content->ncolumns * content->nrows); + exit(EXIT_FAILURE); + } + + *content->cell = (char *) mbvalidate((unsigned char *) cell, + content->opt->encoding); + +#ifdef ENABLE_NLS + if (translate) + *content->header = _(*content->header); +#endif + content->cell++; +} + +/* + * Add a footer to the table. + * + * Footers are added as elements of a singly-linked list, and the content is + * strdup'd, so there is no need to keep the original footer string around. + * + * Footers are never translated by the function. If you want the footer + * translated you must do so yourself, before calling printTableAddFooter. The + * reason this works differently to headers and cells is that footers tend to + * be made of up individually translated components, rather than being + * translated as a whole. + */ +void +printTableAddFooter(printTableContent *const content, const char *footer) +{ + printTableFooter *f; + + f = pg_local_calloc(1, sizeof(*f)); + f->data = pg_strdup(footer); + + if (content->footers == NULL) + content->footers = f; + else + content->footer->next = f; + + content->footer = f; +} + +/* + * Change the content of the last-added footer. + * + * The current contents of the last-added footer are freed, and replaced by the + * content given in *footer. If there was no previous footer, add a new one. + * + * The content is strdup'd, so there is no need to keep the original string + * around. + */ +void +printTableSetFooter(printTableContent *const content, const char *footer) +{ + if (content->footers != NULL) + { + free(content->footer->data); + content->footer->data = pg_strdup(footer); + } + else + printTableAddFooter(content, footer); +} + +/* + * Free all memory allocated to this struct. + * + * Once this has been called, the struct is unusable unless you pass it to + * printTableInit() again. + */ +void +printTableCleanup(printTableContent *content) +{ + free(content->headers); + free(content->cells); + free(content->aligns); + + content->opt = NULL; + content->title = NULL; + content->headers = NULL; + content->cells = NULL; + content->aligns = NULL; + content->header = NULL; + content->cell = NULL; + content->align = NULL; + + if (content->footers) + { + for (content->footer = content->footers; content->footer;) + { + printTableFooter *f; + + f = content->footer; + content->footer = f->next; + free(f->data); + free(f); + } + } + content->footers = NULL; + content->footer = NULL; +} + +/* + * Use this to print just any table in the supported formats. + */ +void +printTable(const printTableContent *cont, FILE *fout, FILE *flog) { - static const char *default_footer[] = {NULL}; FILE *output; bool is_pager = false; - + if (cancel_pressed) return; - if (opt->format == PRINT_NOTHING) + if (cont->opt->format == PRINT_NOTHING) return; - if (!footers) - footers = default_footer; - if (fout == stdout) { - int col_count = 0, - row_count = 0, - lines; - const char *const * ptr; + int lines; - /* rough estimate of columns and rows */ - if (headers) - for (ptr = headers; *ptr; ptr++) - col_count++; - if (cells) - for (ptr = cells; *ptr; ptr++) - row_count++; - if (col_count > 0) - row_count /= col_count; - - if (opt->expanded) - lines = (col_count + 1) * row_count; + if (cont->opt->expanded) + lines = (cont->ncolumns + 1) * cont->nrows; else - lines = row_count + 1; - if (footers && !opt->tuples_only) - for (ptr = footers; *ptr; ptr++) + lines = cont->nrows + 1; + + if (!cont->opt->tuples_only) + { + printTableFooter *f; + + /* + * FIXME -- this is slightly bogus: it counts the number of + * footers, not the number of lines in them. + */ + for (f = cont->footers; f; f = f->next) lines++; - output = PageOutput(lines, opt->pager); + } + + output = PageOutput(lines, cont->opt->pager); is_pager = (output != fout); } else @@ -2031,54 +2157,44 @@ printTable(const char *title, /* print the stuff */ if (flog) - print_aligned_text(title, headers, cells, footers, align, - opt, is_pager, flog); + print_aligned_text(cont, is_pager, flog); - switch (opt->format) + switch (cont->opt->format) { case PRINT_UNALIGNED: - if (opt->expanded) - print_unaligned_vertical(title, headers, cells, footers, align, - opt, output); + if (cont->opt->expanded) + print_unaligned_vertical(cont, output); else - print_unaligned_text(title, headers, cells, footers, align, - opt, output); + print_unaligned_text(cont, output); break; case PRINT_ALIGNED: case PRINT_WRAPPED: - if (opt->expanded) - print_aligned_vertical(title, headers, cells, footers, align, - opt, output); + if (cont->opt->expanded) + print_aligned_vertical(cont, output); else - print_aligned_text(title, headers, cells, footers, align, - opt, is_pager, output); + print_aligned_text(cont, is_pager, output); break; case PRINT_HTML: - if (opt->expanded) - print_html_vertical(title, headers, cells, footers, align, - opt, output); + if (cont->opt->expanded) + print_html_vertical(cont, output); else - print_html_text(title, headers, cells, footers, align, - opt, output); + print_html_text(cont, output); break; case PRINT_LATEX: - if (opt->expanded) - print_latex_vertical(title, headers, cells, footers, align, - opt, output); + if (cont->opt->expanded) + print_latex_vertical(cont, output); else - print_latex_text(title, headers, cells, footers, align, - opt, output); + print_latex_text(cont, output); break; case PRINT_TROFF_MS: - if (opt->expanded) - print_troff_ms_vertical(title, headers, cells, footers, align, - opt, output); + if (cont->opt->expanded) + print_troff_ms_vertical(cont, output); else - print_troff_ms_text(title, headers, cells, footers, align, - opt, output); + print_troff_ms_text(cont, output); break; default: - fprintf(stderr, _("invalid output format (internal error): %d"), opt->format); + fprintf(stderr, _("invalid output format (internal error): %d"), + cont->opt->format); exit(EXIT_FAILURE); } @@ -2086,17 +2202,15 @@ printTable(const char *title, ClosePager(output); } - +/* + * Use this to print query results + * + * It calls printTable with all the things set straight. + */ void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog) { - int ntuples; - int nfields; - int ncells; - const char **headers; - const char **cells; - char **footers; - char *align; + printTableContent cont; int i, r, c; @@ -2104,71 +2218,12 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f if (cancel_pressed) return; - /* extract headers */ - ntuples = PQntuples(result); - nfields = PQnfields(result); + printTableInit(&cont, &opt->topt, opt->title, + PQnfields(result), PQntuples(result)); - headers = pg_local_calloc(nfields + 1, sizeof(*headers)); - - for (i = 0; i < nfields; i++) - { - headers[i] = (char *) mbvalidate((unsigned char *) PQfname(result, i), - opt->topt.encoding); -#ifdef ENABLE_NLS - if (opt->trans_headers) - headers[i] = _(headers[i]); -#endif - } - - /* set cells */ - ncells = ntuples * nfields; - cells = pg_local_calloc(ncells + 1, sizeof(*cells)); - - i = 0; - for (r = 0; r < ntuples; r++) - { - for (c = 0; c < nfields; c++) - { - if (PQgetisnull(result, r, c)) - cells[i] = opt->nullPrint ? opt->nullPrint : ""; - else - { - cells[i] = (char *) - mbvalidate((unsigned char *) PQgetvalue(result, r, c), - opt->topt.encoding); -#ifdef ENABLE_NLS - if (opt->trans_columns && opt->trans_columns[c]) - cells[i] = _(cells[i]); -#endif - } - i++; - } - } - - /* set footers */ - - if (opt->footers) - footers = opt->footers; - else if (!opt->topt.expanded && opt->default_footer) - { - unsigned long total_records; - - footers = pg_local_calloc(2, sizeof(*footers)); - footers[0] = pg_local_malloc(100); - total_records = opt->topt.prior_records + ntuples; - if (total_records == 1) - snprintf(footers[0], 100, _("(1 row)")); - else - snprintf(footers[0], 100, _("(%lu rows)"), total_records); - } - else - footers = NULL; - - /* set alignment */ - align = pg_local_calloc(nfields + 1, sizeof(*align)); - - for (i = 0; i < nfields; i++) + for (i = 0; i < cont.ncolumns; i++) { + char align; Oid ftype = PQftype(result, i); switch (ftype) @@ -2183,27 +2238,59 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f case XIDOID: case CIDOID: case CASHOID: - align[i] = 'r'; + align = 'r'; break; default: - align[i] = 'l'; + align = 'l'; break; } + + printTableAddHeader(&cont, PQfname(result, i), + opt->trans_headers, align); + } + + /* set cells */ + for (r = 0; r < cont.nrows; r++) + { + for (c = 0; c < cont.ncolumns; c++) + { + char *cell; + bool translate; + + if (PQgetisnull(result, r, c)) + cell = opt->nullPrint ? opt->nullPrint : ""; + else + cell = PQgetvalue(result, r, c); + + translate = (opt->trans_columns && opt->trans_columns[c]); + printTableAddCell(&cont, cell, translate); + } } - /* call table printer */ - printTable(opt->title, headers, cells, - (const char *const *) footers, - align, &opt->topt, fout, flog); - - free(headers); - free(cells); - if (footers && !opt->footers) + /* set footers */ + if (opt->footers) { - free(footers[0]); - free(footers); + char **footer; + + for (footer = opt->footers; *footer; footer++) + printTableAddFooter(&cont, *footer); } - free(align); + else if (!opt->topt.expanded && opt->default_footer) + { + unsigned long total_records; + char default_footer[100]; + + total_records = opt->topt.prior_records + cont.nrows; + if (total_records == 1) + snprintf(default_footer, 100, _("(1 row)")); + else + snprintf(default_footer, 100, _("(%lu rows)"), total_records); + + printTableAddFooter(&cont, default_footer); + } + + printTable(&cont, fout, flog); + printTableCleanup(&cont); } @@ -2215,17 +2302,17 @@ setDecimalLocale(void) extlconv = localeconv(); if (*extlconv->decimal_point) - decimal_point = strdup(extlconv->decimal_point); + decimal_point = pg_strdup(extlconv->decimal_point); else decimal_point = "."; /* SQL output standard */ if (*extlconv->grouping && atoi(extlconv->grouping) > 0) - grouping = strdup(extlconv->grouping); + grouping = pg_strdup(extlconv->grouping); else grouping = "3"; /* most common */ /* similar code exists in formatting.c */ if (*extlconv->thousands_sep) - thousands_sep = strdup(extlconv->thousands_sep); + thousands_sep = pg_strdup(extlconv->thousands_sep); /* Make sure thousands separator doesn't match decimal point symbol. */ else if (strcmp(decimal_point, ",") != 0) thousands_sep = ","; diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index da84343df9..f11bc206c5 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.36 2008/05/08 17:04:26 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.37 2008/05/12 22:59:58 alvherre Exp $ */ #ifndef PRINT_H #define PRINT_H @@ -11,11 +11,6 @@ #include "libpq-fe.h" -extern FILE *PageOutput(int lines, unsigned short int pager); -extern void ClosePager(FILE *pagerpipe); - -extern void html_escaped_print(const char *in, FILE *fout); - enum printFormat { PRINT_NOTHING = 0, /* to make sure someone initializes this */ @@ -29,7 +24,7 @@ enum printFormat }; -typedef struct _printTableOpt +typedef struct printTableOpt { enum printFormat format; /* one of the above */ bool expanded; /* expanded/vertical output (if supported by @@ -52,25 +47,42 @@ typedef struct _printTableOpt int columns; /* target width for wrapped format */ } printTableOpt; +/* + * Table footers are implemented as a singly-linked list. + * + * This is so that you don't need to know the number of footers in order to + * initialise the printTableContent struct, which is very convenient when + * preparing complex footers (as in describeOneTableDetails). + */ +typedef struct printTableFooter +{ + char *data; + struct printTableFooter *next; +} printTableFooter; /* - * Use this to print just any table in the supported formats. - * - title is just any string (NULL is fine) - * - headers is the column headings (NULL ptr terminated). It must be given and - * complete since the column count is generated from this. - * - cells are the data cells to be printed. Now you know why the correct - * column count is important - * - footers are lines to be printed below the table - * - align is an 'l' or an 'r' for every column, if the output format needs it. - * (You must specify this long enough. Otherwise anything could happen.) -*/ -void printTable(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *align, - const printTableOpt *opt, FILE *fout, FILE *flog); + * The table content struct holds all the information which will be displayed + * by printTable(). + */ +typedef struct printTableContent +{ + const printTableOpt *opt; + const char *title; /* May be NULL */ + int ncolumns; /* Specified in Init() */ + int nrows; /* Specified in Init() */ + const char **headers; /* NULL-terminated array of header strings */ + const char **header; /* Pointer to the last added header */ + const char **cells; /* NULL-terminated array of cell content + strings */ + const char **cell; /* Pointer to the last added cell */ + printTableFooter *footers; /* Pointer to the first footer */ + printTableFooter *footer; /* Pointer to the last added footer */ + char *aligns; /* Array of alignment specifiers; 'l' or 'r', + one per column */ + char *align; /* Pointer to the last added alignment */ +} printTableContent; - -typedef struct _printQueryOpt +typedef struct printQueryOpt { printTableOpt topt; /* the options above */ char *nullPrint; /* how to print null entities */ @@ -82,15 +94,29 @@ typedef struct _printQueryOpt const bool *trans_columns; /* trans_columns[i-1] => do gettext on col i */ } printQueryOpt; -/* - * Use this to print query results - * - * It calls the printTable above with all the things set straight. - */ -void printQuery(const PGresult *result, const printQueryOpt *opt, - FILE *fout, FILE *flog); -void setDecimalLocale(void); +extern FILE *PageOutput(int lines, unsigned short int pager); +extern void ClosePager(FILE *pagerpipe); + +extern void html_escaped_print(const char *in, FILE *fout); + +extern void printTableInit(printTableContent *const content, + const printTableOpt *opt, const char *title, + const int ncolumns, const int nrows); +extern void printTableAddHeader(printTableContent *const content, + const char *header, const bool translate, const char align); +extern void printTableAddCell(printTableContent *const content, + const char *cell, const bool translate); +extern void printTableAddFooter(printTableContent *const content, + const char *footer); +extern void printTableSetFooter(printTableContent *const content, + const char *footer); +extern void printTableCleanup(printTableContent *const content); +extern void printTable(const printTableContent *cont, FILE *fout, FILE *flog); +extern void printQuery(const PGresult *result, const printQueryOpt *opt, + FILE *fout, FILE *flog); + +extern void setDecimalLocale(void); #ifndef __CYGWIN__ #define DEFAULT_PAGER "more" diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index b1ca5bb71d..ea1fa0f276 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.31 2008/01/01 19:45:56 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.32 2008/05/12 22:59:58 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -229,6 +229,27 @@ executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) return r; } +/* + * "Safe" wrapper around strdup(). Pulled from psql/common.c + */ +char * +pg_strdup(const char *string) +{ + char *tmp; + + if (!string) + { + fprintf(stderr, _("pg_strdup: cannot duplicate null pointer (internal error)\n")); + exit(EXIT_FAILURE); + } + tmp = strdup(string); + if (!tmp) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + return tmp; +} /* * Check yes/no answer in a localized way. 1=yes, 0=no, -1=neither. @@ -274,7 +295,6 @@ yesno_prompt(const char *question) } } - /* * SetCancelConn *