简体   繁体   English

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

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

Say I have created some user-defined types in the DB,假设我在数据库中创建了一些用户定义的类型,

ie CREATE TYPE abc...CREATE TYPE abc...

Is it then possible to determine if the user-defined type exists or not?那么是否可以确定用户定义的类型是否存在? Perhaps, using any of the postgres information tables?也许,使用任何 postgres 信息表?

The main reason for this is since PostgreSQL does not seem to support CREATE OR REPLACE TYPE... , and if a certain type gets created more than once, I want to be able to drop the existing one first, then re-load the new one.主要原因是因为 PostgreSQL 似乎不支持CREATE OR REPLACE TYPE... ,如果多次创建某个类型,我希望能够先删除现有的,然后重新加载新的一。

I add here the complete solution for creating types in a simple script, without the need of creating a function just for this purpose.我在这里添加了在简单脚本中创建类型的完整解决方案,而无需为此目的创建函数。

--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$$;

The simplest solution I've found so far that copes with schemas, inspired by @Cromax's answer, is this:到目前为止,我发现的最简单的解决方案是在@Cromax 的回答的启发下处理模式:

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

Just what you might expect really - we just wrap the CREATE TYPE statement in an exception handler so it doesn't abort the current transaction.正如您所期望的那样 - 我们只是将 CREATE TYPE 语句包装在异常处理程序中,因此它不会中止当前事务。

You can look in the pg_type table:您可以查看pg_type表:

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

If that is true then abc exists.如果这是真的,那么abc存在。

Indeed, Postgres does not have CREATE OR REPLACE functionality for types.事实上,Postgres 没有类型的CREATE OR REPLACE功能。 So the best approach is to drop it:所以最好的方法是放弃它:

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

Simple solution is always the best one.简单的解决方案总是最好的。

To solve @rog's dilemma to @bluish's answer it could be more appropriate to make use of regtype data type.为了解决regtyperegtype的回答的困境,使用regtype数据类型可能更合适。 Consider this:考虑一下:

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 clause is like SELECT , but it discards results, so basically we're checking if it is possible to cast 'my_schema.my_type' (or just 'my_type' if you don't care to be schema specific) to actual registered type. PERFORM子句类似于SELECT ,但它会丢弃结果,因此基本上我们正在检查是否可以将'my_schema.my_type' (或者如果您不关心特定于架构'my_type'则只是'my_type' )转换为实际注册的类型。 If the type does exist, then nothing "wrong" will happen and because of RETURN whole block will end—no changes, since the type my_type is already there.如果类型确实存在,那么不会发生任何“错误”,并且由于RETURN整个块将结束——没有变化,因为类型my_type已经存在。 But if the cast is not possible, then there will be thrown error code 42704 which has label of undefined_object .但如果无法进行转换,则会抛出错误代码42704 ,其标签为undefined_object So in the next lines we try to catch that error and if that happens, we simply create our new data type.因此,在接下来的几行中,我们尝试捕获该错误,如果发生这种情况,我们只需创建新的数据类型。

-- 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();
-----------------------------------

I'm trying to do the same thing, ensure a type exists.我正在尝试做同样的事情,确保类型存在。

I started psql with the --echo-hidden ( -E ) option and entered \\dT :我使用--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)

If you are using schemas and search_path (I am) then you'll probably need to keep the pg_catalog.pg_type_is_visible(t.oid) check.如果您正在使用模式和 search_path(我是),那么您可能需要保持pg_catalog.pg_type_is_visible(t.oid)检查。 I don't know what all the conditions in the WHERE are doing, but they didn't seem relevant to my case.我不知道 WHERE 中的所有条件都在做什么,但它们似乎与我的案例无关。 Currently using:目前使用:

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

A more generic solution更通用的解决方案

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;

and then you can call it like this:然后你可以这样称呼它:

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

This plays well with schemas, and avoids exception handling:这与模式配合得很好,并避免了异常处理:

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
$$;

Inspired by @Cromax's answer, here's an alternative solution using the system catalog information function to_regtype that avoids the extra overhead of an exception clause but still checks the correct schema for the type's existence:受@Cromax 回答的启发,这是使用系统目录信息 function to_regtype的替代解决方案,该解决方案避免了异常子句的额外开销,但仍检查类型是否存在的正确模式:

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

In the case of the default public schema being used, it would look like:在使用默认public模式的情况下,它看起来像:

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

Another alternative另一种选择

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';

Continue with bluish code, we also need to check if DB has such a type in current schema.继续蓝色代码,我们还需要检查 DB 在当前模式中是否有这样的类型。 Because current code will not create type if db has same type in any of db schemas.因为如果 db 在任何 db 模式中具有相同的类型,当前代码将不会创建类型。 So full universal code will look like this:所以完整的通用代码将如下所示:

$$
    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