const char *oid,
bool verbose)
{
+ bool retval = false;
PQExpBufferData buf;
PGresult *res = NULL;
printTableOpt myopt = pset.popt.topt;
PQExpBufferData title;
PQExpBufferData tmpbuf;
int cols;
- int numrows = 0;
+ int attname_col = -1, /* column indexes in "res" */
+ atttype_col = -1,
+ attrdef_col = -1,
+ attnotnull_col = -1,
+ attcoll_col = -1,
+ attidentity_col = -1,
+ isindexkey_col = -1,
+ indexdef_col = -1,
+ fdwopts_col = -1,
+ attstorage_col = -1,
+ attstattarget_col = -1,
+ attdescr_col = -1;
+ int numrows;
struct
{
int16 checks;
char relreplident;
} tableinfo;
bool show_column_details = false;
- bool retval;
-
- retval = false;
myopt.default_footer = false;
/* This output looks confusing in expanded mode. */
goto error_return; /* not an error, just return early */
}
+ /* Identify whether we should print collation, nullable, default vals */
+ if (tableinfo.relkind == RELKIND_RELATION ||
+ tableinfo.relkind == RELKIND_VIEW ||
+ tableinfo.relkind == RELKIND_MATVIEW ||
+ tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
+ tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
+ tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
+ show_column_details = true;
+
/*
- * Get column info
+ * Get per-column info
*
- * You need to modify value of "firstvcol" which will be defined below if
- * you are adding column(s) preceding to verbose-only columns.
+ * Since the set of query columns we need varies depending on relkind and
+ * server version, we compute all the column numbers on-the-fly. Column
+ * number variables for columns not fetched are left as -1; this avoids
+ * duplicative test logic below.
*/
- printfPQExpBuffer(&buf, "SELECT a.attname,");
- appendPQExpBufferStr(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod),"
- "\n (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
- "\n FROM pg_catalog.pg_attrdef d"
- "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
- "\n a.attnotnull, a.attnum,");
- if (pset.sversion >= 90100)
- appendPQExpBufferStr(&buf, "\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
- " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
- else
- appendPQExpBufferStr(&buf, "\n NULL AS attcollation");
- if (pset.sversion >= 100000)
- appendPQExpBufferStr(&buf, ",\n a.attidentity");
- else
- appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attidentity");
+ cols = 0;
+ printfPQExpBuffer(&buf, "SELECT a.attname");
+ attname_col = cols++;
+ appendPQExpBufferStr(&buf, ",\n pg_catalog.format_type(a.atttypid, a.atttypmod)");
+ atttype_col = cols++;
+
+ if (show_column_details)
+ {
+ appendPQExpBufferStr(&buf,
+ ",\n (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
+ "\n FROM pg_catalog.pg_attrdef d"
+ "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)"
+ ",\n a.attnotnull");
+ attrdef_col = cols++;
+ attnotnull_col = cols++;
+ if (pset.sversion >= 90100)
+ appendPQExpBufferStr(&buf, ",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
+ " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
+ else
+ appendPQExpBufferStr(&buf, ",\n NULL AS attcollation");
+ attcoll_col = cols++;
+ if (pset.sversion >= 100000)
+ appendPQExpBufferStr(&buf, ",\n a.attidentity");
+ else
+ appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attidentity");
+ attidentity_col = cols++;
+ }
if (tableinfo.relkind == RELKIND_INDEX ||
tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
+ {
+ if (pset.sversion >= 110000)
+ {
+ appendPQExpBuffer(&buf, ",\n CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key",
+ oid,
+ gettext_noop("yes"),
+ gettext_noop("no"));
+ isindexkey_col = cols++;
+ }
appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
- else
- appendPQExpBufferStr(&buf, ",\n NULL AS indexdef");
+ indexdef_col = cols++;
+ }
+ /* FDW options for foreign table column, only for 9.2 or later */
if (tableinfo.relkind == RELKIND_FOREIGN_TABLE && pset.sversion >= 90200)
+ {
appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
" '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM "
" pg_catalog.pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
- else
- appendPQExpBufferStr(&buf, ",\n NULL AS attfdwoptions");
+ fdwopts_col = cols++;
+ }
if (verbose)
{
appendPQExpBufferStr(&buf, ",\n a.attstorage");
- appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
+ attstorage_col = cols++;
+
+ /* stats target, if relevant to relkind */
+ if (tableinfo.relkind == RELKIND_RELATION ||
+ tableinfo.relkind == RELKIND_INDEX ||
+ tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
+ tableinfo.relkind == RELKIND_MATVIEW ||
+ tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
+ tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
+ attstattarget_col = cols++;
+ }
/*
* In 9.0+, we have column comments for: relations, views, composite
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)");
+ {
+ appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)");
+ attdescr_col = cols++;
+ }
}
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
break;
}
- /* Set the number of columns, and their names */
- headers[0] = gettext_noop("Column");
- headers[1] = gettext_noop("Type");
- cols = 2;
-
- if (tableinfo.relkind == RELKIND_RELATION ||
- tableinfo.relkind == RELKIND_VIEW ||
- tableinfo.relkind == RELKIND_MATVIEW ||
- tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
- tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
- tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
+ /* Fill headers[] with the names of the columns we will output */
+ cols = 0;
+ headers[cols++] = gettext_noop("Column");
+ headers[cols++] = gettext_noop("Type");
+ if (show_column_details)
{
headers[cols++] = gettext_noop("Collation");
headers[cols++] = gettext_noop("Nullable");
headers[cols++] = gettext_noop("Default");
- show_column_details = true;
}
-
- if (tableinfo.relkind == RELKIND_INDEX ||
- tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
+ if (isindexkey_col >= 0)
+ headers[cols++] = gettext_noop("Key?");
+ if (indexdef_col >= 0)
headers[cols++] = gettext_noop("Definition");
-
- if (tableinfo.relkind == RELKIND_FOREIGN_TABLE && pset.sversion >= 90200)
+ if (fdwopts_col >= 0)
headers[cols++] = gettext_noop("FDW options");
-
- if (verbose)
- {
+ if (attstorage_col >= 0)
headers[cols++] = gettext_noop("Storage");
- if (tableinfo.relkind == RELKIND_RELATION ||
- tableinfo.relkind == RELKIND_INDEX ||
- tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
- tableinfo.relkind == RELKIND_MATVIEW ||
- tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
- tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- headers[cols++] = gettext_noop("Stats target");
- /* Column comments, if the relkind supports this feature. */
- if (tableinfo.relkind == RELKIND_RELATION ||
- tableinfo.relkind == RELKIND_VIEW ||
- tableinfo.relkind == RELKIND_MATVIEW ||
- tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
- tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
- tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- headers[cols++] = gettext_noop("Description");
- }
+ if (attstattarget_col >= 0)
+ headers[cols++] = gettext_noop("Stats target");
+ if (attdescr_col >= 0)
+ headers[cols++] = gettext_noop("Description");
+
+ Assert(cols <= lengthof(headers));
printTableInit(&cont, &myopt, title.data, cols, numrows);
printTableInitialized = true;
for (i = 0; i < cols; i++)
printTableAddHeader(&cont, headers[i], true, 'l');
- /* Get view_def if table is a view or materialized view */
- if ((tableinfo.relkind == RELKIND_VIEW ||
- tableinfo.relkind == RELKIND_MATVIEW) && verbose)
- {
- PGresult *result;
-
- printfPQExpBuffer(&buf,
- "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
- oid);
- result = PSQLexec(buf.data);
- if (!result)
- goto error_return;
-
- if (PQntuples(result) > 0)
- view_def = pg_strdup(PQgetvalue(result, 0, 0));
-
- PQclear(result);
- }
-
/* Generate table cells to be printed */
for (i = 0; i < numrows; i++)
{
/* Column */
- printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
+ printTableAddCell(&cont, PQgetvalue(res, i, attname_col), false, false);
/* Type */
- printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
+ printTableAddCell(&cont, PQgetvalue(res, i, atttype_col), false, false);
/* Collation, Nullable, Default */
if (show_column_details)
char *identity;
char *default_str = "";
- printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
+ printTableAddCell(&cont, PQgetvalue(res, i, attcoll_col), false, false);
- printTableAddCell(&cont, strcmp(PQgetvalue(res, i, 3), "t") == 0 ? "not null" : "", false, false);
+ printTableAddCell(&cont,
+ strcmp(PQgetvalue(res, i, attnotnull_col), "t") == 0 ? "not null" : "",
+ false, false);
- identity = PQgetvalue(res, i, 6);
+ identity = PQgetvalue(res, i, attidentity_col);
if (!identity[0])
/* (note: above we cut off the 'default' string at 128) */
- default_str = PQgetvalue(res, i, 2);
+ default_str = PQgetvalue(res, i, attrdef_col);
else if (identity[0] == ATTRIBUTE_IDENTITY_ALWAYS)
default_str = "generated always as identity";
else if (identity[0] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
printTableAddCell(&cont, default_str, false, false);
}
- /* Expression for index column */
- if (tableinfo.relkind == RELKIND_INDEX ||
- tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
- printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
+ /* Info for index columns */
+ if (isindexkey_col >= 0)
+ printTableAddCell(&cont, PQgetvalue(res, i, isindexkey_col), true, false);
+ if (indexdef_col >= 0)
+ printTableAddCell(&cont, PQgetvalue(res, i, indexdef_col), false, false);
- /* FDW options for foreign table column, only for 9.2 or later */
- if (tableinfo.relkind == RELKIND_FOREIGN_TABLE && pset.sversion >= 90200)
- printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
+ /* FDW options for foreign table columns */
+ if (fdwopts_col >= 0)
+ printTableAddCell(&cont, PQgetvalue(res, i, fdwopts_col), false, false);
/* Storage and Description */
- if (verbose)
+ if (attstorage_col >= 0)
{
- int firstvcol = 9;
- char *storage = PQgetvalue(res, i, firstvcol);
+ char *storage = PQgetvalue(res, i, attstorage_col);
/* these strings are literal in our syntax, so not translated. */
printTableAddCell(&cont, (storage[0] == 'p' ? "plain" :
(storage[0] == 'e' ? "external" :
"???")))),
false, false);
+ }
- /* Statistics target, if the relkind supports this feature */
- if (tableinfo.relkind == RELKIND_RELATION ||
- tableinfo.relkind == RELKIND_INDEX ||
- tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
- tableinfo.relkind == RELKIND_MATVIEW ||
- tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
- tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- {
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
- false, false);
- }
+ /* Statistics target, if the relkind supports this feature */
+ if (attstattarget_col >= 0)
+ printTableAddCell(&cont, PQgetvalue(res, i, attstattarget_col),
+ false, false);
- /* Column comments, if the relkind supports this feature. */
- if (tableinfo.relkind == RELKIND_RELATION ||
- tableinfo.relkind == RELKIND_VIEW ||
- tableinfo.relkind == RELKIND_MATVIEW ||
- tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
- tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
- tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
- printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
- false, false);
- }
+ /* Column comments, if the relkind supports this feature */
+ if (attdescr_col >= 0)
+ printTableAddCell(&cont, PQgetvalue(res, i, attdescr_col),
+ false, false);
}
/* Make footers */
}
}
+ /* Get view_def if table is a view or materialized view */
+ if ((tableinfo.relkind == RELKIND_VIEW ||
+ tableinfo.relkind == RELKIND_MATVIEW) && verbose)
+ {
+ PGresult *result;
+
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
+ oid);
+ result = PSQLexec(buf.data);
+ if (!result)
+ goto error_return;
+
+ if (PQntuples(result) > 0)
+ view_def = pg_strdup(PQgetvalue(result, 0, 0));
+
+ PQclear(result);
+ }
+
if (view_def)
{
PGresult *result = NULL;
CREATE INDEX test_index1 on testschema.test_default_tab (id);
CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE regress_tblspace;
\d testschema.test_index1
-Index "testschema.test_index1"
- Column | Type | Definition
---------+--------+------------
- id | bigint | id
+ Index "testschema.test_index1"
+ Column | Type | Key? | Definition
+--------+--------+------+------------
+ id | bigint | yes | id
btree, for table "testschema.test_default_tab"
\d testschema.test_index2
-Index "testschema.test_index2"
- Column | Type | Definition
---------+--------+------------
- id | bigint | id
+ Index "testschema.test_index2"
+ Column | Type | Key? | Definition
+--------+--------+------+------------
+ id | bigint | yes | id
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
\d testschema.test_index1
-Index "testschema.test_index1"
- Column | Type | Definition
---------+--------+------------
- id | bigint | id
+ Index "testschema.test_index1"
+ Column | Type | Key? | Definition
+--------+--------+------+------------
+ id | bigint | yes | id
btree, for table "testschema.test_default_tab"
\d testschema.test_index2
-Index "testschema.test_index2"
- Column | Type | Definition
---------+--------+------------
- id | bigint | id
+ Index "testschema.test_index2"
+ Column | Type | Key? | Definition
+--------+--------+------+------------
+ id | bigint | yes | id
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
\d testschema.test_index1
-Index "testschema.test_index1"
- Column | Type | Definition
---------+---------+------------
- id | integer | id
+ Index "testschema.test_index1"
+ Column | Type | Key? | Definition
+--------+---------+------+------------
+ id | integer | yes | id
btree, for table "testschema.test_default_tab"
\d testschema.test_index2
-Index "testschema.test_index2"
- Column | Type | Definition
---------+---------+------------
- id | integer | id
+ Index "testschema.test_index2"
+ Column | Type | Key? | Definition
+--------+---------+------+------------
+ id | integer | yes | id
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
\d testschema.test_index1
-Index "testschema.test_index1"
- Column | Type | Definition
---------+---------+------------
- id | integer | id
+ Index "testschema.test_index1"
+ Column | Type | Key? | Definition
+--------+---------+------+------------
+ id | integer | yes | id
btree, for table "testschema.test_default_tab"
\d testschema.test_index2
-Index "testschema.test_index2"
- Column | Type | Definition
---------+---------+------------
- id | integer | id
+ Index "testschema.test_index2"
+ Column | Type | Key? | Definition
+--------+---------+------+------------
+ id | integer | yes | id
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
\d testschema.test_index1
-Index "testschema.test_index1"
- Column | Type | Definition
---------+--------+------------
- id | bigint | id
+ Index "testschema.test_index1"
+ Column | Type | Key? | Definition
+--------+--------+------+------------
+ id | bigint | yes | id
btree, for table "testschema.test_default_tab"
\d testschema.test_index2
-Index "testschema.test_index2"
- Column | Type | Definition
---------+--------+------------
- id | bigint | id
+ Index "testschema.test_index2"
+ Column | Type | Key? | Definition
+--------+--------+------+------------
+ id | bigint | yes | id
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
SET default_tablespace TO '';
ALTER TABLE testschema.test_tab ADD CONSTRAINT test_tab_pkey PRIMARY KEY (id);
\d testschema.test_tab_unique
-Index "testschema.test_tab_unique"
- Column | Type | Definition
---------+---------+------------
- id | integer | id
+ Index "testschema.test_tab_unique"
+ Column | Type | Key? | Definition
+--------+---------+------+------------
+ id | integer | yes | id
unique, btree, for table "testschema.test_tab"
Tablespace: "regress_tblspace"
\d testschema.test_tab_pkey
-Index "testschema.test_tab_pkey"
- Column | Type | Definition
---------+---------+------------
- id | integer | id
+ Index "testschema.test_tab_pkey"
+ Column | Type | Key? | Definition
+--------+---------+------+------------
+ id | integer | yes | id
primary key, btree, for table "testschema.test_tab"
SELECT * FROM testschema.test_tab;