DROP EVENT TRIGGER trig_test_event_trigger_table_have_primary_key;
CREATE OR REPLACE FUNCTION test_event_trigger_table_have_primary_key ()
RETURNS event_trigger
LANGUAGE plpgsql
AS $$
DECLARE
obj record;
object_types text[];
table_name text;
BEGIN
FOR obj IN
SELECT
*
FROM
pg_event_trigger_ddl_commands ()
LOOP
RAISE NOTICE 'classid: % objid: %,object_type: %
object_identity: % schema_name: % command_tag: %' , obj.classid , obj.objid , obj.object_type , obj.object_identity , obj.schema_name , obj.command_tag;
IF obj.object_type ~ 'table' THEN
table_name := obj.object_identity;
END IF;
object_types := object_types || obj.object_type;
END LOOP;
RAISE NOTICE 'table name: %' , table_name;
IF EXISTS (
SELECT
FROM
pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid
AND a.attnum = ANY (i.indkey)
WHERE
i.indisprimary
AND i.indrelid = table_name::regclass) IS FALSE THEN
RAISE EXCEPTION ' no primary key, this table not created';
END IF;
END;
$$;
CREATE EVENT TRIGGER trig_test_event_trigger_table_have_primary_key ON ddl_command_end
WHEN TAG IN ('CREATE TABLE')
EXECUTE FUNCTION test_event_trigger_table_have_primary_key ();
演示:
DROP TABLE a3;
DROP TABLE a4;
DROP TABLE a5;
CREATE TABLE a3 (
a int
);
CREATE TABLE a4 (
a int PRIMARY KEY
);
CREATE TABLE a5 (
a1 int UNIQUE
);
CREATE OR REPLACE FUNCTION block_ddl()
RETURNS event_trigger
LANGUAGE plpgsql AS
$$
BEGIN
RAISE EXCEPTION 'It is forbidden to create tables using command: %', tg_tag ;
END;
$$;
CREATE EVENT TRIGGER AdHocTables_forbidden
ON ddl_command_end
WHEN TAG IN ('CREATE TABLE AS', 'SELECT INTO')
EXECUTE FUNCTION block_ddl();
CREATE OR REPLACE FUNCTION pk_enforced()
RETURNS event_trigger
LANGUAGE plpgsql AS
$$
DECLARE r RECORD;
BEGIN
RAISE NOTICE 'Caught command %', (SELECT current_query());
FOR r IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
RAISE NOTICE 'Caught inside command % (%)', r.command_tag, r.object_identity;
END LOOP;
RAISE EXCEPTION 'Blocking the Creation';
END;
$$;
CREATE EVENT TRIGGER pk_is_mandatory
ON ddl_command_end
WHEN TAG IN ('CREATE TABLE', 'ALTER TABLE')
EXECUTE FUNCTION pk_enforced();
CREATE OR REPLACE FUNCTION public.pk_enforced()
RETURNS event_trigger
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
obj RECORD;
table_name text;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands ()
LOOP
IF obj.schema_name = 'pg_temp' THEN
return;
END IF;
IF obj.object_type ~ 'table' THEN
table_name := obj.object_identity;
END IF;
END LOOP;
IF NOT EXISTS (
SELECT
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY (i.indkey)
WHERE i.indrelid = table_name::regclass
AND (i.indisprimary OR i.indisunique)) THEN
RAISE EXCEPTION 'A primary key or a unique constraint is mandatory to perform % on %.', tg_tag, obj.object_identity;
END IF;
END;
$BODY$;
CREATE OR REPLACE FUNCTION public.block_ddl()
RETURNS event_trigger
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
obj RECORD;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands ()
LOOP
IF obj.schema_name = 'pg_temp' THEN
return;
END IF;
END LOOP;
RAISE EXCEPTION 'DDL command ''%'' is blocked.', tg_tag ;
END;
$BODY$;
CREATE EVENT TRIGGER pk_is_mandatory ON DDL_COMMAND_END
WHEN TAG IN ('CREATE TABLE', 'ALTER TABLE')
EXECUTE PROCEDURE public.pk_enforced();
CREATE EVENT TRIGGER adhoctables_forbidden ON DDL_COMMAND_END
WHEN TAG IN ('CREATE TABLE AS', 'SELECT INTO')
EXECUTE PROCEDURE public.block_ddl();
2条答案
按热度按时间jm81lzqq1#
演示:
将只创建表
a4
。相关职位:PL/pgSQL检查行是否存在
https://wiki.postgresql.org/wiki/Retrieve_primary_key_columns
wwtsj6pe2#
该逻辑适用于多个event triggers(另请参见documentation for the create command)。
首先要注意的是这可以应用到的DDL命令,所有这些命令都记录在here中。
第1部分:
CREATE TABLE AS
和SELECT INTO
如果我没有记错的话,
CREATE TABLE AS
和SELECT INTO
从来没有在创建的表上添加约束,它们必须被一个总是引发异常的事件触发器阻塞。请注意,您可以将触发器定义为
ON
ddl_command_start ',这会使它稍微快一点,但与我在最后发布的完整代码不匹配。关于其余的解释,请看下一个不那么直接的部分。
第2部分:常规
CREATE TABLE
和ALTER TABLE
这种情况更为复杂,因为我们只想阻止一些命令,而不是所有命令。
下面的函数和事件触发器执行以下操作:
1.输出正在传递的整个命令。
1.将命令分解为子部分。
要做到这一点,它使用
pg_event_trigger_ddl_commands()
(这里的文档),顺便说一句,这就是为什么这个触发器必须在ddl_command_end
上的原因。您会注意到,在添加主键时,也会捕获
CREATE INDEX
。1.对于下面的函数,在所有情况下都会引发异常以阻止创建(这样您就可以测试它,而不必删除每次创建的表)。
下面是代码:
补充说明:
通过测试
schema_name
是否为pg_temp
,可以防止在临时表上实施这些约束。完整的代码,包括这个测试,并与信贷建的功能,他张贴: