From 62faf64abf9cc1ddd74a3fbccc62777e103ece85 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat Date: Thu, 5 Dec 2024 14:31:36 +0530 Subject: [PATCH v14 09/15] WIP: Do not print empty columns table for a property graph A property graph does not have any columns by itself. Thus it doesn't need the table describing columns in \d[+] output. The patch needs following work 1. Move the code to collect column information in a separate function, which can be skipped for property graphs. This avoids one indentation level and makes describeOneTableDetails() easy to follow. 2. Even though the borders are suppressed there is an extra blank line in \d output. Avoid that. It requires some adjustment to printTableOpt or some minion of printTable to avoid printing everything related to column description table. OR If we decide that we want to output a different description (like sequences) we need to decide what that description should be and then implement the same. Ashutosh Bapat --- src/bin/psql/describe.c | 307 +++++++++--------- .../expected/create_property_graph.out | 6 +- 2 files changed, 164 insertions(+), 149 deletions(-) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 0cd24d80009..3fd55d0435e 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -1873,128 +1873,6 @@ describeOneTableDetails(const char *schemaname, 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 per-column info - * - * 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. - */ - 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) - { - /* use "pretty" mode for expression to avoid excessive parentheses */ - appendPQExpBufferStr(&buf, - ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)" - "\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++; - 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"); - 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 (pset.sversion >= 120000) - appendPQExpBufferStr(&buf, ",\n a.attgenerated"); - else - appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated"); - attgenerated_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"); - indexdef_col = cols++; - } - /* FDW options for foreign table column */ - if (tableinfo.relkind == RELKIND_FOREIGN_TABLE) - { - 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"); - fdwopts_col = cols++; - } - if (verbose) - { - appendPQExpBufferStr(&buf, ",\n a.attstorage"); - attstorage_col = cols++; - - /* compression info, if relevant to relkind */ - if (pset.sversion >= 140000 && - !pset.hide_compression && - (tableinfo.relkind == RELKIND_RELATION || - tableinfo.relkind == RELKIND_PARTITIONED_TABLE || - tableinfo.relkind == RELKIND_MATVIEW)) - { - appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression"); - attcompression_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 - * types, and foreign tables (cf. CommentObject() in comment.c). - */ - 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) - { - appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)"); - attdescr_col = cols++; - } - } - - appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a"); - appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid); - appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;"); - - res = PSQLexec(buf.data); - if (!res) - goto error_return; - numrows = PQntuples(res); - /* Make title */ switch (tableinfo.relkind) { @@ -2061,32 +1939,171 @@ describeOneTableDetails(const char *schemaname, break; } - /* Fill headers[] with the names of the columns we will output */ + /* 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 per-column info + * + * 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. Property graph relations do not have + * columns. + */ cols = 0; - headers[cols++] = gettext_noop("Column"); - headers[cols++] = gettext_noop("Type"); - if (show_column_details) + numrows = 0; + if (tableinfo.relkind != RELKIND_PROPGRAPH) { - headers[cols++] = gettext_noop("Collation"); - headers[cols++] = gettext_noop("Nullable"); - headers[cols++] = gettext_noop("Default"); + 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) + { + /* use "pretty" mode for expression to avoid excessive parentheses */ + appendPQExpBufferStr(&buf, + ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)" + "\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++; + 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"); + 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 (pset.sversion >= 120000) + appendPQExpBufferStr(&buf, ",\n a.attgenerated"); + else + appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated"); + attgenerated_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"); + indexdef_col = cols++; + } + /* FDW options for foreign table column */ + if (tableinfo.relkind == RELKIND_FOREIGN_TABLE) + { + 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"); + fdwopts_col = cols++; + } + if (verbose) + { + appendPQExpBufferStr(&buf, ",\n a.attstorage"); + attstorage_col = cols++; + + /* compression info, if relevant to relkind */ + if (pset.sversion >= 140000 && + !pset.hide_compression && + (tableinfo.relkind == RELKIND_RELATION || + tableinfo.relkind == RELKIND_PARTITIONED_TABLE || + tableinfo.relkind == RELKIND_MATVIEW)) + { + appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression"); + attcompression_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 types, and foreign tables (cf. CommentObject() in + * comment.c). + */ + 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) + { + appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)"); + attdescr_col = cols++; + } + } + + appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a"); + appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid); + appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;"); + + res = PSQLexec(buf.data); + if (!res) + goto error_return; + numrows = PQntuples(res); + + /* Fill headers[] with the names of the columns we will output */ + cols = 0; + if (tableinfo.relkind != RELKIND_PROPGRAPH) + { + /* A property graph does not project any columns by itself. */ + 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"); + } + if (isindexkey_col >= 0) + headers[cols++] = gettext_noop("Key?"); + if (indexdef_col >= 0) + headers[cols++] = gettext_noop("Definition"); + if (fdwopts_col >= 0) + headers[cols++] = gettext_noop("FDW options"); + if (attstorage_col >= 0) + headers[cols++] = gettext_noop("Storage"); + if (attcompression_col >= 0) + headers[cols++] = gettext_noop("Compression"); + if (attstattarget_col >= 0) + headers[cols++] = gettext_noop("Stats target"); + if (attdescr_col >= 0) + headers[cols++] = gettext_noop("Description"); + + Assert(cols <= lengthof(headers)); } - if (isindexkey_col >= 0) - headers[cols++] = gettext_noop("Key?"); - if (indexdef_col >= 0) - headers[cols++] = gettext_noop("Definition"); - if (fdwopts_col >= 0) - headers[cols++] = gettext_noop("FDW options"); - if (attstorage_col >= 0) - headers[cols++] = gettext_noop("Storage"); - if (attcompression_col >= 0) - headers[cols++] = gettext_noop("Compression"); - if (attstattarget_col >= 0) - headers[cols++] = gettext_noop("Stats target"); - if (attdescr_col >= 0) - headers[cols++] = gettext_noop("Description"); - - Assert(cols <= lengthof(headers)); + + /* + * Like a property graph, if the relation doesn't have any columns, + * suppress borders. + */ + if (cols <= 0 && numrows <= 0) + myopt.border = 0; printTableInit(&cont, &myopt, title.data, cols, numrows); printTableInitialized = true; diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out index d35c02b462c..824695358f8 100644 --- a/src/test/regress/expected/create_property_graph.out +++ b/src/test/regress/expected/create_property_graph.out @@ -654,13 +654,11 @@ ERROR: "pg_type" is not a property graph -- TODO \d g1 Property graph "create_property_graph_tests.g1" - Column | Type ---------+------ + \d+ g1 Property graph "create_property_graph_tests.g1" - Column | Type | Storage ---------+------+--------- + Property graph definition: CREATE PROPERTY GRAPH create_property_graph_tests.g1 -- 2.39.5