Skip to content

Commit 62cb9e2

Browse files
authored
fix: include relation title in wildcard describe output (#159)
* fix: Relation information missing in wildcard describes * chore: add change to changelog * refactor: make relkind to name mapping protected variable * feat: add tests
1 parent 8e1f5db commit 62cb9e2

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11

2+
Unreleased
3+
==========
4+
5+
Bug fixes:
6+
----------
7+
* Include relation type/name titles in `\d` and `\d+` describe output so wildcard describe results retain per-relation context.
8+
29
2.2.1 (2025-04-27)
310
==================
411

pgspecial/dbcommands.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
],
2626
)
2727

28+
_RELKIND_TO_NAME = {
29+
"r": "Table",
30+
"p": "Partitioned table",
31+
"v": "View",
32+
"m": "Materialized view",
33+
"i": "Index",
34+
"I": "Partitioned index",
35+
"S": "Sequence",
36+
"f": "Foreign table",
37+
"c": "Composite type",
38+
"t": "TOAST table",
39+
}
40+
2841
log = logging.getLogger(__name__)
2942

3043

@@ -1805,7 +1818,10 @@ def describe_one_table_details(cur, schema_name, relation_name, oid, verbose):
18051818
if verbose and tableinfo.reloptions:
18061819
status.append(f"Options: {tableinfo.reloptions}\n")
18071820

1808-
return (None, cells, headers, "".join(status))
1821+
relkind_name = _RELKIND_TO_NAME.get(tableinfo.relkind, "Relation")
1822+
title = f'{relkind_name} "{schema_name}.{relation_name}"'
1823+
1824+
return (title, cells, headers, "".join(status))
18091825

18101826

18111827
def sql_name_pattern(pattern):

tests/test_specials.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def test_slash_d_verbose(executor):
159159
@dbtest
160160
def test_slash_d_table_1(executor):
161161
results = executor(r"\d tbl1")
162-
title = None
162+
title = 'Table "public.tbl1"'
163163
rows = [
164164
["id1", "integer", " not null"],
165165
["txt1", "text", " not null"],
@@ -173,7 +173,7 @@ def test_slash_d_table_1(executor):
173173
@dbtest
174174
def test_slash_d_table_2(executor):
175175
results = executor(r"\d tbl2")
176-
title = None
176+
title = 'Table "public.tbl2"'
177177
rows = [
178178
["id2", "integer", " not null default nextval('tbl2_id2_seq'::regclass)"],
179179
["txt2", "text", ""],
@@ -184,6 +184,22 @@ def test_slash_d_table_2(executor):
184184
assert results == expected
185185

186186

187+
@dbtest
188+
def test_slash_d_wildcard(executor):
189+
results = executor(r"\d tbl*")
190+
# tbl* matches tbl1, tbl2, tbl2_id2_seq (sequence), tbl3, tbl3_c3_excl (index)
191+
# executor flattens each block as [title, rows, headers, status]
192+
# so titles appear at indices 0, 4, 8, ...
193+
titles = results[0::4]
194+
assert titles == [
195+
'Table "public.tbl1"',
196+
'Table "public.tbl2"',
197+
'Sequence "public.tbl2_id2_seq"',
198+
'Table "public.tbl3"',
199+
'Index "public.tbl3_c3_excl"',
200+
]
201+
202+
187203
@dbtest
188204
def test_slash_d_test_generated_default(executor):
189205
results = executor(r"\d schema3.test_generated_default")
@@ -200,10 +216,10 @@ def test_slash_d_test_generated_default(executor):
200216

201217
@dbtest
202218
def test_slash_d_table_verbose_1(executor):
203-
title = None
204219
headers = ["Column", "Type", "Modifiers", "Storage", "Stats target", "Description"]
205220

206221
results = executor(r"\d+ tbl1")
222+
title = 'Table "public.tbl1"'
207223
rows = [
208224
["id1", "integer", " not null", "plain", None, None],
209225
["txt1", "text", " not null", "extended", None, None],
@@ -213,6 +229,7 @@ def test_slash_d_table_verbose_1(executor):
213229
assert results == expected
214230

215231
results = executor(r'\d+ "Inh1"')
232+
title = 'Table "public.Inh1"'
216233
rows = [
217234
["id1", "integer", " not null", "plain", None, None],
218235
["txt1", "text", " not null", "extended", None, None],
@@ -225,10 +242,10 @@ def test_slash_d_table_verbose_1(executor):
225242

226243
@dbtest
227244
def test_slash_d_table_verbose_2(executor):
228-
title = None
229245
headers = ["Column", "Type", "Modifiers", "Storage", "Stats target", "Description"]
230246

231247
results = executor(r"\d+ tbl2")
248+
title = 'Table "public.tbl2"'
232249
rows = [
233250
[
234251
"id2",
@@ -245,6 +262,7 @@ def test_slash_d_table_verbose_2(executor):
245262
assert results == expected
246263

247264
results = executor(r"\d+ inh2")
265+
title = 'Table "public.inh2"'
248266
rows = [
249267
["id1", "integer", " not null", "plain", None, None],
250268
["txt1", "text", " not null", "extended", None, None],
@@ -266,7 +284,7 @@ def test_slash_d_table_verbose_2(executor):
266284

267285
@dbtest
268286
def test_slash_d_view_verbose(executor):
269-
title = None
287+
title = 'View "public.vw1"'
270288
headers = ["Column", "Type", "Modifiers", "Storage", "Description"]
271289

272290
results = executor(r"\d+ vw1")
@@ -283,7 +301,7 @@ def test_slash_d_view_verbose(executor):
283301
@dbtest
284302
def test_slash_d_table_with_exclusion(executor):
285303
results = executor(r"\d tbl3")
286-
title = None
304+
title = 'Table "public.tbl3"'
287305
rows = [["c3", "circle", ""]]
288306
headers = ["Column", "Type", "Modifiers"]
289307
status = 'Indexes:\n "tbl3_c3_excl" EXCLUDE USING gist (c3 WITH &&)\n'
@@ -294,7 +312,7 @@ def test_slash_d_table_with_exclusion(executor):
294312
@dbtest
295313
def test_slash_d_table_2_in_schema(executor):
296314
results = executor(r"\d schema2.tbl2")
297-
title = None
315+
title = 'Table "schema2.tbl2"'
298316
rows = [
299317
[
300318
"id2",

0 commit comments

Comments
 (0)