diff --git a/changelog.rst b/changelog.rst index 69a0072..bbdcfb0 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,4 +1,11 @@ +Unreleased +========== + +Bug fixes: +---------- +* Include relation type/name titles in `\d` and `\d+` describe output so wildcard describe results retain per-relation context. + 2.2.1 (2025-04-27) ================== diff --git a/pgspecial/dbcommands.py b/pgspecial/dbcommands.py index 24cc9f0..7f02e6c 100644 --- a/pgspecial/dbcommands.py +++ b/pgspecial/dbcommands.py @@ -25,6 +25,19 @@ ], ) +_RELKIND_TO_NAME = { + "r": "Table", + "p": "Partitioned table", + "v": "View", + "m": "Materialized view", + "i": "Index", + "I": "Partitioned index", + "S": "Sequence", + "f": "Foreign table", + "c": "Composite type", + "t": "TOAST table", +} + log = logging.getLogger(__name__) @@ -1805,7 +1818,10 @@ def describe_one_table_details(cur, schema_name, relation_name, oid, verbose): if verbose and tableinfo.reloptions: status.append(f"Options: {tableinfo.reloptions}\n") - return (None, cells, headers, "".join(status)) + relkind_name = _RELKIND_TO_NAME.get(tableinfo.relkind, "Relation") + title = f'{relkind_name} "{schema_name}.{relation_name}"' + + return (title, cells, headers, "".join(status)) def sql_name_pattern(pattern): diff --git a/tests/test_specials.py b/tests/test_specials.py index c93583d..6ce5a03 100755 --- a/tests/test_specials.py +++ b/tests/test_specials.py @@ -159,7 +159,7 @@ def test_slash_d_verbose(executor): @dbtest def test_slash_d_table_1(executor): results = executor(r"\d tbl1") - title = None + title = 'Table "public.tbl1"' rows = [ ["id1", "integer", " not null"], ["txt1", "text", " not null"], @@ -173,7 +173,7 @@ def test_slash_d_table_1(executor): @dbtest def test_slash_d_table_2(executor): results = executor(r"\d tbl2") - title = None + title = 'Table "public.tbl2"' rows = [ ["id2", "integer", " not null default nextval('tbl2_id2_seq'::regclass)"], ["txt2", "text", ""], @@ -184,6 +184,22 @@ def test_slash_d_table_2(executor): assert results == expected +@dbtest +def test_slash_d_wildcard(executor): + results = executor(r"\d tbl*") + # tbl* matches tbl1, tbl2, tbl2_id2_seq (sequence), tbl3, tbl3_c3_excl (index) + # executor flattens each block as [title, rows, headers, status] + # so titles appear at indices 0, 4, 8, ... + titles = results[0::4] + assert titles == [ + 'Table "public.tbl1"', + 'Table "public.tbl2"', + 'Sequence "public.tbl2_id2_seq"', + 'Table "public.tbl3"', + 'Index "public.tbl3_c3_excl"', + ] + + @dbtest def test_slash_d_test_generated_default(executor): results = executor(r"\d schema3.test_generated_default") @@ -200,10 +216,10 @@ def test_slash_d_test_generated_default(executor): @dbtest def test_slash_d_table_verbose_1(executor): - title = None headers = ["Column", "Type", "Modifiers", "Storage", "Stats target", "Description"] results = executor(r"\d+ tbl1") + title = 'Table "public.tbl1"' rows = [ ["id1", "integer", " not null", "plain", None, None], ["txt1", "text", " not null", "extended", None, None], @@ -213,6 +229,7 @@ def test_slash_d_table_verbose_1(executor): assert results == expected results = executor(r'\d+ "Inh1"') + title = 'Table "public.Inh1"' rows = [ ["id1", "integer", " not null", "plain", None, None], ["txt1", "text", " not null", "extended", None, None], @@ -225,10 +242,10 @@ def test_slash_d_table_verbose_1(executor): @dbtest def test_slash_d_table_verbose_2(executor): - title = None headers = ["Column", "Type", "Modifiers", "Storage", "Stats target", "Description"] results = executor(r"\d+ tbl2") + title = 'Table "public.tbl2"' rows = [ [ "id2", @@ -245,6 +262,7 @@ def test_slash_d_table_verbose_2(executor): assert results == expected results = executor(r"\d+ inh2") + title = 'Table "public.inh2"' rows = [ ["id1", "integer", " not null", "plain", None, None], ["txt1", "text", " not null", "extended", None, None], @@ -266,7 +284,7 @@ def test_slash_d_table_verbose_2(executor): @dbtest def test_slash_d_view_verbose(executor): - title = None + title = 'View "public.vw1"' headers = ["Column", "Type", "Modifiers", "Storage", "Description"] results = executor(r"\d+ vw1") @@ -283,7 +301,7 @@ def test_slash_d_view_verbose(executor): @dbtest def test_slash_d_table_with_exclusion(executor): results = executor(r"\d tbl3") - title = None + title = 'Table "public.tbl3"' rows = [["c3", "circle", ""]] headers = ["Column", "Type", "Modifiers"] status = 'Indexes:\n "tbl3_c3_excl" EXCLUDE USING gist (c3 WITH &&)\n' @@ -294,7 +312,7 @@ def test_slash_d_table_with_exclusion(executor): @dbtest def test_slash_d_table_2_in_schema(executor): results = executor(r"\d schema2.tbl2") - title = None + title = 'Table "schema2.tbl2"' rows = [ [ "id2",