Postgres pg_catalog 常量查找的策略

Strategies for Postgres pg_catalog constant lookups

短版: 我在 pg_catalog 上花费了一些时间,并希望显示使用字符代码的扩展定义。例如 "composite type" 而不是 "c" 用于 pg_class.relkind。我试过自定义函数和查找汤 table。我希望得到一些建议,并可能指出我忽略的明显事情。

Postgres 11.5,部署在 RDS 上(无超级用户。)

更长的版本: 对于我们的项目,我正在编写一些客户端代码生成器和报告屏幕,这意味着我需要深入研究 pg_type、pg_class、pg_attribute 等等。由于我认为是历史原因,pg_catalog 中的许多 table 和列名称是......不透明的。许多字段包含需要查找或记忆的字符代码。例如,pg_class.relkind 包含值 I、S、c、f、i、m、p、r、t 或 v 之一。嗯?我可以记住那些意思并在脑海中翻译它们,但计算机可以更轻松地做到这一点。所以,我想我会写一个函数:

CREATE OR REPLACE FUNCTION data.relkind_name (relkind text, out relkind_name text)
  RETURNS text
 AS $$
SELECT CASE
  WHEN relkind = 'r' THEN 'table'
  WHEN relkind = 'i' THEN 'index'
  WHEN relkind = 'S' THEN 'sequence'
  WHEN relkind = 't' THEN 'TOAST table'
  WHEN relkind = 'v' THEN 'view'
  WHEN relkind = 'm' THEN 'materialized view'
  WHEN relkind = 'c' THEN 'composite type'
  WHEN relkind = 'f' THEN 'foreign table'
  WHEN relkind = 'p' THEN 'partitioned table'
  WHEN relkind = 'I' THEN 'partitioned index'
  ELSE 'Unexpected relkind ' || relkind
END;
$$ LANGUAGE sql;

ALTER FUNCTION data.relkind_name (relkind text, out relkind_name text) OWNER TO user_bender;

那...很好。它有效,但我不喜欢这样的功能有几个原因 1) 数据被烘焙到代码中,而不是数据结构中。所以,你不能以任何方式 reuse/display/validate 它。 2)我需要为每个常量类型定制一个函数。这让我想到了下一个想法,查找汤table。

BEGIN;
DROP TABLE IF EXISTS data.constant CASCADE;

CREATE TABLE IF NOT EXISTS data.constant (
    theme text NOT NULL DEFAULT NULL,
    code text NOT NULL DEFAULT NULL, 
    label text NOT NULL DEFAULT NULL,

    PRIMARY KEY (theme, code)
);

ALTER TABLE data.constant OWNER TO user_change_structure;
COMMIT;

在进一步讨论之前,我会规定 grab-all 查找 table 通常值得嘲笑 不是我会用动态的、用户驱动的数据做的事情。因为不好。太糟了。但在这个狭窄的案例中,这似乎是一个可靠的想法:

下面是 pg_catalog 中几个常量列表的一些设置:

INSERT INTO constant
    (theme,code,label)

VALUES
    ('typcategory','A','Array types'),
    ('typcategory','B','Boolean types'),
    ('typcategory','C','Composite types'),
    ('typcategory','D','Date/time types'),
    ('typcategory','E','Enum types'),
    ('typcategory','G','Geometric types'),
    ('typcategory','I','Network address types'),
    ('typcategory','N','Numeric types'),
    ('typcategory','P','Pseudo-types'),
    ('typcategory','R','Range types'),
    ('typcategory','S','String types'),
    ('typcategory','T','Timespan types'),
    ('typcategory','U','User-defined types'),
    ('typcategory','V','Bit-string types'),
    ('typcategory','X','unknown type'),
    ('relkind','r','ordinary table'),
    ('relkind','i','index'),
    ('relkind','S','sequence'),
    ('relkind','t','TOAST table'),
    ('relkind','v','view'),
    ('relkind','m','materialized view'),
    ('relkind','c','composite type'),
    ('relkind','f','foreign table'),
    ('relkind','p','partitioned table'),
    ('relkind','I','partitioned index');

由于数据在table中,你可以用正常的方式做正常的事情。或者甚至使用 Postgres 的精彩 string_agg 功能:

  select theme,
         string_agg(code, ', ' order by code) as constants 
    from constant 
group by theme
order by theme;

relkind I, S, c, f, i, m, p, r, t, v
typcategory A, B, C, D, E, G, I, N, P, R, S, T, U, V, X

或进行查找的简单查询:

-- I want a default/error result label if there is no match.
select coalesce((select label from constant where theme = 'relkind' and code  = 'X'),
                'Undefined')

可以包装成一个函数:

DROP FUNCTION IF EXISTS data.lookup (theme text, code text);

CREATE OR REPLACE FUNCTION data.lookup (theme text, code text)
    RETURNS TEXT 
AS $$
-- I want a default/error result label if there is no match, hence the subquery.
select coalesce(
                (select label 
                   from constant 
                  where theme =  and
                        code  = ),
                'Undefined')
$$ LANGUAGE sql;

ALTER FUNCTION data.lookup (theme text, code text)  OWNER TO user_bender;

最后,对目录 table 的查询给出了人类可读的结果:

select relowner::regrole,
        relnamespace::regnamespace,
        relname,
        lookup('relkind',relkind) as relkind_name,
        reltype::regtype

  from pg_class

从上面你会看到,我找到了一些oid魔法施法工具,还有一些系统信息功能。我希望使用 lookup() 函数来填补一些空白。

如果有评论或建议,我将不胜感激,即使这相当于 "throw all of that out, there's a better way."

作为记录,我检查了自定义类型,magic::castings,CREATE DOMAIN(不适用),ENUM(不适用且不上诉。)我目前正在排除构建一堆自定义视图,因为感觉这会让下一个人更难修改我的代码。 (一个查找函数看起来不太好学。)

您可以按照自己喜欢的方式进行,如果您的味蕾对一次查找的反应更好 table,那就这样吧。

我觉得你的动机部分是为了好玩,因为经过一些接触后,你将能够轻松记住常用的单字母代码。

在这种情况下,我建议多玩一些类型:

您可以创建一个自定义类型,其内部表示与 "char" 一样,但类型输出函数会生成长描述。类型输入函数将理解单个字符串和长名称。

relkind和其他短代码会有这样的类型。

然后在 "char" 和新类型之间创建 IMPLICIT 强制转换 WITHOUT FUNCTION。如果需要,您还可以创建 (EXPLICIT) 到 text.

的强制转换

整个过程与 regclassregtype 和相关的便利类型非常相似。

如果不出意外,这是一篇很好的介绍如何破解 PostgreSQL 的文章。