【问题标题】:Strategies for Postgres pg_catalog constant lookupsPostgres pg_catalog 常量查找的策略
【发布时间】:2019-09-05 03:38:23
【问题描述】:

短版: 我在 pg_catalog 中花费了一些时间,并希望显示使用 char 代码的扩展定义。例如 pg_class.relkind 的“复合类型”而不是“c”。我尝试过自定义函数和查找汤表。我希望得到一些建议,并且很可能会指出我忽略的明显事情。

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

加长版: 对于我们的项目,我正在编写相当多的客户端代码生成器和报告屏幕,这意味着我需要深入研究 pg_type、pg_class、pg_attribute 等等。由于我认为是历史原因,pg_catalog 中的许多表名和列名都是....不透明的。并且大量字段包括需要查找或记忆的字符代码。例如,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)数据被写入代码,而不是数据结构。因此,您不能以任何方式重用/显示/验证它。 2)我需要为每个常量类型提供一个自定义函数。这让我想到了下一个想法,查找汤表。

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;

在继续之前,我将规定抓取所有查找表通常值得嘲笑。这不是我会用动态的、用户驱动的数据做的事情。因为不好。太糟了。但在这个狭隘的案例中,这似乎是一个可靠的想法:

  • 数据被烘焙到 Postgres 中,并且仅在主要版本中发生变化。

  • 这些数据都不会消失,或者至少不会消失。

  • 一旦我遇到感兴趣的事情,添加一组新常量非常容易。

以下是 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');

由于数据在表格中,您可以以正常的方式做正常的事情。甚至可以使用 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 = $1 and
                        code  = $2),
                'Undefined')
$$ LANGUAGE sql;

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

然后,最后,对目录表进行查询,得到人类可读的结果:

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

  from pg_class

从上面你会看到,我发现了一些oid魔法施法工具,以及一些系统信息函数。我正在寻找使用 lookup() 函数来填补一些空白。

如果您提出评论或建议,我将不胜感激,即使这相当于“抛弃所有这些,有更好的方法”。

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

【问题讨论】:

  • 我会选择第一个函数(出于性能原因将其定义为immutable)。与其他解决方案相比,更易于维护、更易于理解且代码更少。我不明白为什么它不应该是“可重复使用的”

标签: postgresql catalog


【解决方案1】:

您可以按照自己的方式进行操作,如果您的味蕾对单个查找表的反应更好,那就这样吧。

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

在这种情况下,我建议更多地使用类型:

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

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

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

整个事情将与regclassregtype 和相关的便利类型非常相似。

如果没有别的,这是一个很好的 PostgreSQL 黑客介绍。

【讨论】:

  • “嬉戏”,说得好。是的,我这样做主要是为了弄清 pg_catalog 的细节,希望能同化它们。我觉得你的想法很有趣,比如: relkind::relkindname 这正是我想做的,你做到了。我昨天尝试了这个,并且有点让它工作。据我所知,当我真正想要的是 char 的别名时,我必须使用自定义复合类型。使用自定义 CREATE TYPE,我可以拥有基于函数的 CREATE CAST。
  • 我想我失去了我的实验,但在我的脑海中,它是这样的:CREATE TYPE relkindname AS (label text);使用函数 relkind_get_name(char) 创建 CAST (char as relkindname);我没有让这个工作顺利。输入和输出类型看起来相当繁琐,使用具有一个属性的复合类型会产生不受欢迎的后果。似乎当您转换复合(甚至是行)类型时,您会得到(parens)
  • 从表名中选择表名::文本;这使您将行转换为文本,但带有括号。 (如您所知。)我发现任何复合类型都相同。因此,当我的函数返回“base”时,relkind::relkindname 可能会返回 (base)。显然,我有点胡思乱想,我没有找到任何可以帮助的例子。你能指出我更具体的吗?无论如何,我都在从挖掘这些细节中学习。我想了解演员面糊能够将一行转换为特定格式。说,只有五个字段中的三个通过演员表。非常感谢!
  • 我考虑的不是复合类型,而是用 C 函数实现的常规类型。听起来令人生畏,但在这种情况下它只会是类型输入和输出函数,其他所有内容都可以从 "char" 获取。
  • 我只是在检查一下。是的,部署在 RDS 上,所以没有自定义 C 代码或超级用户。无论如何,我可能会自己尝试学习,我现在找到了一些在 C 中创建新基本类型的非常详细的示例。如果 SQL 级别的 CREATE TYPE 允许使用类型别名系统,那就太好了。 CREATE TYPE (relkind name LIKE text); -- 或者类似的东西就目前而言,复合类型有很大的帮助,但我所追求的只是一种为 CAST 制作签名的方法。
猜你喜欢
  • 1970-01-01
  • 2013-03-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 2015-12-12
相关资源
最近更新 更多