繁体   English   中英

检查 PostgreSQL 中是否已存在用户定义类型

[英]Check if a user-defined type already exists in PostgreSQL

假设我在数据库中创建了一些用户定义的类型,

CREATE TYPE abc...

那么是否可以确定用户定义的类型是否存在? 也许,使用任何 postgres 信息表?

主要原因是因为 PostgreSQL 似乎不支持CREATE OR REPLACE TYPE... ,如果多次创建某个类型,我希望能够先删除现有的,然后重新加载新的一。

我在这里添加了在简单脚本中创建类型的完整解决方案,而无需为此目的创建函数。

--create types
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'my_type') THEN
        CREATE TYPE my_type AS
        (
            --my fields here...
        );
    END IF;
    --more types here...
END$$;

到目前为止,我发现的最简单的解决方案是在@Cromax 的回答的启发下处理模式:

DO $$ BEGIN
    CREATE TYPE my_type AS (/* fields go here */);
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

正如您所期望的那样 - 我们只是将 CREATE TYPE 语句包装在异常处理程序中,因此它不会中止当前事务。

您可以查看pg_type表:

select exists (select 1 from pg_type where typname = 'abc');

如果这是真的,那么abc存在。

事实上,Postgres 没有类型的CREATE OR REPLACE功能。 所以最好的方法是放弃它:

DROP TYPE IF EXISTS YOUR_TYPE;
CREATE TYPE YOUR_TYPE AS (
    id      integer,
    field   varchar
);

简单的解决方案总是最好的。

为了解决regtyperegtype的回答的困境,使用regtype数据类型可能更合适。 考虑一下:

DO $$ BEGIN
    PERFORM 'my_schema.my_type'::regtype;
EXCEPTION
    WHEN undefined_object THEN
        CREATE TYPE my_schema.my_type AS (/* fields go here */);
END $$;

PERFORM子句类似于SELECT ,但它会丢弃结果,因此基本上我们正在检查是否可以将'my_schema.my_type' (或者如果您不关心特定于架构'my_type'则只是'my_type' )转换为实际注册的类型。 如果类型确实存在,那么不会发生任何“错误”,并且由于RETURN整个块将结束——没有变化,因为类型my_type已经存在。 但如果无法进行转换,则会抛出错误代码42704 ,其标签为undefined_object 因此,在接下来的几行中,我们尝试捕获该错误,如果发生这种情况,我们只需创建新的数据类型。

-- All of this to create a type if it does not exist
CREATE OR REPLACE FUNCTION create_abc_type() RETURNS integer AS $$
DECLARE v_exists INTEGER;

BEGIN
    SELECT into v_exists (SELECT 1 FROM pg_type WHERE typname = 'abc');
    IF v_exists IS NULL THEN
        CREATE TYPE abc AS ENUM ('height', 'weight', 'distance');
    END IF;
    RETURN v_exists;
END;
$$ LANGUAGE plpgsql;

-- Call the function you just created
SELECT create_abc_type();

-- Remove the function you just created
DROP function create_abc_type();
-----------------------------------

我正在尝试做同样的事情,确保类型存在。

我使用--echo-hidden ( -E ) 选项启动 psql 并输入\\dT

$ psql -E
psql (9.1.9)
testdb=> \dT
********* QUERY **********
SELECT n.nspname as "Schema",
  pg_catalog.format_type(t.oid, NULL) AS "Name",
  pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
FROM pg_catalog.pg_type t
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
  AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
      AND n.nspname <> 'pg_catalog'
      AND n.nspname <> 'information_schema'
  AND pg_catalog.pg_type_is_visible(t.oid)
ORDER BY 1, 2;
**************************
 List of data types
 Schema |       Name       | Description 
--------+------------------+-------------
 public | errmsg_agg_state | 
(1 row)

如果您正在使用模式和 search_path(我是),那么您可能需要保持pg_catalog.pg_type_is_visible(t.oid)检查。 我不知道 WHERE 中的所有条件都在做什么,但它们似乎与我的案例无关。 目前使用:

SELECT 1 FROM pg_catalog.pg_type as t
   WHERE typname = 'mytype' AND pg_catalog.pg_type_is_visible(t.oid);

更通用的解决方案

CREATE OR REPLACE FUNCTION create_type(name text, _type text) RETURNS 
integer AS $$
DECLARE v_exists INTEGER;

BEGIN
    SELECT into v_exists (SELECT 1 FROM pg_type WHERE typname = name);
    IF v_exists IS NULL THEN
            EXECUTE format('CREATE TYPE %I AS %s', name, _type);
    END IF;
    RETURN v_exists;
END;
$$ LANGUAGE plpgsql;

然后你可以这样称呼它:

select create_type('lwm2m_instancetype', 'enum (''single'',''multiple'')');

这与模式配合得很好,并避免了异常处理:

DO $$
BEGIN
    IF NOT EXISTS (
      SELECT 1 FROM pg_type t
      LEFT JOIN pg_namespace p ON t.typnamespace=p.oid
      WHERE t.typname='my_type' AND p.nspname='my_schema'
    ) THEN
        CREATE TYPE my_schema.my_type AS (/* fields go here */);
    END IF;
END
$$;

受@Cromax 回答的启发,这是使用系统目录信息 function to_regtype的替代解决方案,该解决方案避免了异常子句的额外开销,但仍检查类型是否存在的正确模式:

DO $$ BEGIN
    IF to_regtype('my_schema.abc') IS NULL THEN
        CREATE TYPE my_schema.abc ... ;
    END IF;
END $$;

在使用默认public模式的情况下,它看起来像:

DO $$ BEGIN
    IF to_regtype('abc') IS NULL THEN
        CREATE TYPE abc ... ;
    END IF;
END $$;

另一种选择

WITH namespace AS(
    SELECT oid 
        FROM pg_namespace 
        WHERE nspname = 'my_schema'
),
type_name AS (
    SELECT 1 type_exist  
        FROM pg_type 
        WHERE typname = 'my_type' AND typnamespace = (SELECT * FROM namespace)
)
SELECT EXISTS (SELECT * FROM type_name);

你应该试试这个:

SELECT * from pg_enum WHERE enumlabel='WHAT YOU WANT';

继续蓝色代码,我们还需要检查 DB 在当前模式中是否有这样的类型。 因为如果 db 在任何 db 模式中具有相同的类型,当前代码将不会创建类型。 所以完整的通用代码将如下所示:

$$
    BEGIN
        IF NOT EXISTS(select 
                      from pg_type
                      WHERE typname = 'YOUR_ENUM_NAME'
                        AND typnamespace in
                            (SELECT oid FROM pg_catalog.pg_namespace where nspname = "current_schema"())) THEN
            CREATE TYPE YOUR_ENUM_NAME AS ENUM (....list of values ....);
        END IF;
    END
$$;```

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM