diff --git a/README.md b/README.md index cade780..deaba6f 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Install the extension. ```sql CREATE EXTENSION "pg_full_inherit" SCHEMA "abstract" - VERSION '1.0'; + VERSION '1.1'; ``` [Learn more about an extension and control file](https://postgrespro.ru/docs/postgresql/current/extend-extensions) diff --git a/README.ru.md b/README.ru.md index 33e5481..53a1403 100644 --- a/README.ru.md +++ b/README.ru.md @@ -65,7 +65,7 @@ ALTER ROLE "postgres" SET search_path TO "public", "abstract"; ```sql CREATE EXTENSION "pg_full_inherit" SCHEMA "abstract" - VERSION '1.0'; + VERSION '1.1'; ``` [Подробнее про расширение и файл control](https://postgrespro.ru/docs/postgresql/current/extend-extensions) diff --git a/dist/pg_full_inherit--1.0--1.1.sql b/dist/pg_full_inherit--1.0--1.1.sql new file mode 100644 index 0000000..7bdc5bd --- /dev/null +++ b/dist/pg_full_inherit--1.0--1.1.sql @@ -0,0 +1,294 @@ +/* +-- =================== GET_TABLE_NAME =================== +*/ +CREATE FUNCTION @extschema@.get_table_name ("relid" REGCLASS, "is_full" BOOLEAN = TRUE) + RETURNS TEXT +AS $$ +BEGIN + RETURN ( + SELECT CASE WHEN "is_full" THEN format('%I.%I', n.nspname, c.relname) + ELSE format('%I', c.relname) + END + FROM pg_class c JOIN pg_namespace n on c.relnamespace = n.oid + WHERE c.oid = "relid"); +END +$$ +LANGUAGE plpgsql +RETURNS NULL ON NULL INPUT; +/* +=================== NAME =================== +*/ +DROP FUNCTION @extschema@.get_child_constraint_name ("name" TEXT, "parent" TEXT, "child" TEXT); + +CREATE FUNCTION @extschema@.get_child_constraint_name ("name" TEXT, "parent" REGCLASS, "child" REGCLASS) + RETURNS TEXT +AS $$ +BEGIN + RETURN regexp_replace("name", '^' || @extschema@.get_table_name("parent", FALSE), @extschema@.get_table_name("child", FALSE)); +END +$$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +DROP FUNCTION @extschema@.get_child_trigger_name ("name" TEXT, "parent" TEXT, "child" TEXT); + +CREATE FUNCTION @extschema@.get_child_trigger_name ("name" TEXT, "parent" REGCLASS, "child" REGCLASS) + RETURNS TEXT +AS $$ +BEGIN + RETURN "name"; +END +$$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; +/* +=================== DEF =================== +*/ +DROP FUNCTION @extschema@.get_child_trigger_def ("parentdef" TEXT, "parentname" TEXT, "parent" TEXT, "child" TEXT); + +CREATE FUNCTION @extschema@.get_child_trigger_def ("parentdef" TEXT, "parentname" TEXT, "parent" REGCLASS, "child" REGCLASS) + RETURNS TEXT +AS $$ +BEGIN + RETURN regexp_replace ( + replace ("parentdef", @extschema@.get_table_name("parent", TRUE), @extschema@.get_table_name("child", TRUE)), + '(CREATE (CONSTRAINT )?TRIGGER) ' || quote_ident ("parentname"), + '\1 ' || quote_ident (@extschema@.get_child_trigger_name("parentname", "parent", "child"))); +END +$$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; +/* +=================== GET_INHERIT_TRIGGERS =================== +*/ +CREATE OR REPLACE FUNCTION @extschema@.get_inherit_triggers () + RETURNS TABLE +( + "parentid" OID, + "parentrelid" OID, + "parentname" TEXT, + "parentdef" TEXT, + "childid" OID, + "childrelid" OID, + "childname" TEXT, + "childdef" TEXT, + "is_inherited" BOOL +) +AS $$ +BEGIN + RETURN QUERY + WITH "triggers" AS ( + SELECT "t"."oid", "t"."tgrelid", "t"."tgname", pg_get_triggerdef("t"."oid") AS "tgdef" + FROM "pg_trigger" "t" + WHERE "t"."tgisinternal" = FALSE) + SELECT "pc"."oid" AS "parentid", + "i"."inhparent" AS "parentrelid", + "pc"."tgname"::TEXT AS "parentname", + "pc"."tgdef" AS "parentdef", + "cc"."oid" AS "childid", + "i"."inhrelid" AS "childrelid", + "cc"."tgname"::TEXT AS "childname", + "cc"."tgdef" AS "childdef", + "cc"."tgdef" IS NOT NULL AS "is_inherited" + FROM "pg_inherits" "i" + LEFT JOIN "triggers" "pc" ON "i"."inhparent" = "pc"."tgrelid" + LEFT JOIN "triggers" "cc" ON "i"."inhrelid" = "cc"."tgrelid" + AND @extschema@.get_child_trigger_def("pc"."tgdef", "pc"."tgname":: TEXT, "i"."inhparent"::REGCLASS, "i"."inhrelid"::REGCLASS) = "cc"."tgdef" + WHERE "pc"."oid" IS NOT NULL OR "cc"."oid" IS NOT NULL; +END +$$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; +/* +=================== EVENT_TRIGGER_ADD_INHERIT_CONSTRAINTS =================== +*/ +CREATE OR REPLACE FUNCTION @extschema@.event_trigger_add_inherit_constraints () + RETURNS EVENT_TRIGGER +AS $$ +DECLARE + "command" RECORD; + "parent" OID; + "child" OID; + "constraint" RECORD; + "name" TEXT; + "query" TEXT; + "constraints" REFCURSOR; +BEGIN + FOR "command" IN + SELECT * FROM pg_event_trigger_ddl_commands () + LOOP + IF "command".in_extension = TRUE THEN + CONTINUE; + END IF; + + IF "command".command_tag = 'CREATE TABLE' THEN + "child" = "command".objid; + OPEN "constraints" FOR + SELECT * FROM @extschema@.get_inherit_constraints() "c" + WHERE "c"."childrelid" = "child" AND "c"."is_inherited" = FALSE + LIMIT 1; + ELSEIF "command".command_tag = 'ALTER TABLE' THEN + "parent" = "command".objid; + "child" = "command".objid; + OPEN "constraints" FOR + SELECT * FROM @extschema@.get_inherit_constraints() "c" + WHERE ("c"."parentrelid" = "parent" OR "c"."childrelid" = "child") + AND "c"."is_inherited" = FALSE + LIMIT 1; + ELSE + CONTINUE; + END IF; + + LOOP + FETCH NEXT FROM "constraints" INTO "constraint"; + EXIT WHEN "constraint" IS NULL; + + "name" = @extschema@.get_child_constraint_name("constraint"."parentname", "constraint"."parentrelid"::REGCLASS, "constraint"."childrelid"::REGCLASS); + "query" = format('ALTER TABLE %1s ADD CONSTRAINT %2I %3s;', "constraint"."childrelid"::REGCLASS, "name", "constraint"."parentdef"); + RAISE NOTICE USING MESSAGE = format('-- ADD CONSTRAINT %1I TO %2s TABLE FROM %3s TABLE', "name", "constraint"."childrelid"::REGCLASS, "constraint"."parentrelid"::REGCLASS); + RAISE NOTICE USING MESSAGE = "query"; + EXECUTE "query"; + END LOOP; + CLOSE "constraints"; + END LOOP; +END; +$$ +LANGUAGE plpgsql +VOLATILE; +/* +=================== EVENT_TRIGGER_ADD_INHERIT_TRIGGERS =================== +*/ +CREATE OR REPLACE FUNCTION @extschema@.event_trigger_add_inherit_triggers () + RETURNS EVENT_TRIGGER +AS $$ +DECLARE + "command" RECORD; + "triggerid" OID; + "child" OID; + "trigger" RECORD; + "name" TEXT; + "query" TEXT; + "triggers" REFCURSOR; +BEGIN + FOR "command" IN + SELECT * FROM pg_event_trigger_ddl_commands () + LOOP + IF "command".in_extension = TRUE THEN + CONTINUE; + END IF; + + IF "command".command_tag = 'CREATE TABLE' THEN + "child" = "command".objid; + OPEN "triggers" FOR + SELECT * FROM @extschema@.get_inherit_triggers() "t" + WHERE "t"."childrelid" = "child" AND "t"."is_inherited" = FALSE; + ELSEIF "command".command_tag = 'ALTER TABLE' THEN + "child" = "command".objid; + OPEN "triggers" FOR + SELECT * FROM @extschema@.get_inherit_triggers() "t" + WHERE "t"."childrelid" = "child" AND "t"."is_inherited" = FALSE; + ELSEIF "command".command_tag = 'CREATE TRIGGER' THEN + "triggerid" = "command".objid; + OPEN "triggers" FOR + SELECT * FROM @extschema@.get_inherit_triggers() "t" + WHERE "t"."parentid" = "triggerid" AND "t"."is_inherited" = FALSE; + ELSE + CONTINUE; + END IF; + + LOOP + FETCH NEXT FROM "triggers" INTO "trigger"; + EXIT WHEN "trigger" IS NULL; + + "name" = @extschema@.get_child_trigger_name("trigger"."parentname", "trigger"."parentrelid"::REGCLASS, "trigger"."childrelid"::REGCLASS); + "query" = @extschema@.get_child_trigger_def("trigger"."parentdef", "trigger"."parentname", "trigger"."parentrelid"::REGCLASS, "trigger"."childrelid"::REGCLASS); + RAISE NOTICE USING MESSAGE = format('-- ADD TRIGGER %1I TO %2s TABLE FROM %3s TABLE', "name", "trigger"."childrelid"::REGCLASS, "trigger"."parentrelid"::REGCLASS); + RAISE NOTICE USING MESSAGE = "query"; + EXECUTE "query"; + END LOOP; + CLOSE "triggers"; + END LOOP; +END; +$$ +LANGUAGE plpgsql +VOLATILE; +/* +=================== EVENT_TRIGGER_DROP_INHERIT_CONSTRAINTS =================== +*/ +CREATE OR REPLACE FUNCTION @extschema@.event_trigger_drop_inherit_constraints () + RETURNS EVENT_TRIGGER +AS $$ +DECLARE + "object" RECORD; + "parent" OID; + "child" OID; + "name" TEXT; + "query" TEXT; + "schema" TEXT; + "table" TEXT; +BEGIN + FOR "object" IN + SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF "object".object_type = 'table constraint' THEN + "schema" = "object".address_names[1]; + "table" = "object".address_names[2]; + "parent" = format('%1I.%2I', "schema", "table")::REGCLASS::OID; + "name" = "object".address_names[3]; + FOR "child" IN + SELECT inhrelid FROM pg_inherits WHERE inhparent = "parent" + LOOP + "name" = @extschema@.get_child_constraint_name("name", "parent"::REGCLASS, "child"::REGCLASS); + "query" = format('ALTER TABLE %1s DROP CONSTRAINT IF EXISTS %2I;', "child"::REGCLASS, "name"); + RAISE NOTICE USING MESSAGE = format('-- DROP CONSTRAINT %1I FROM %2s TABLE BASED ON DEPENDENCY ON %3s TABLE', "name", "child"::REGCLASS, "parent"::REGCLASS); + RAISE NOTICE USING MESSAGE = "query"; + EXECUTE "query"; + END LOOP; + END IF; + END LOOP; +END; +$$ +LANGUAGE plpgsql +VOLATILE; +/* +=================== EVENT_TRIGGER_DROP_INHERIT_TRIGGERS =================== +*/ +CREATE OR REPLACE FUNCTION @extschema@.event_trigger_drop_inherit_triggers () + RETURNS EVENT_TRIGGER +AS $$ +DECLARE + "object" RECORD; + "parent" OID; + "child" OID; + "name" TEXT; + "query" TEXT; + "schema" TEXT; + "table" TEXT; +BEGIN + FOR "object" IN + SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF "object".object_type = 'trigger' THEN + "schema" = "object".address_names[1]; + "table" = "object".address_names[2]; + "parent" = format('%1I.%2I', "schema", "table")::REGCLASS::OID; + "name" = "object".address_names[3]; + FOR "child" IN + SELECT inhrelid FROM pg_inherits WHERE inhparent = "parent" + LOOP + "name" = @extschema@.get_child_trigger_name("name", "parent"::REGCLASS, "child"::REGCLASS); + "query" = format('DROP TRIGGER IF EXISTS %1I ON %2s;', "name", "child"::REGCLASS); + RAISE NOTICE USING MESSAGE = format('-- DROP TRIGGER %1I FROM %2s TABLE BASED ON DEPENDENCY ON %3s TABLE', "name", "child"::REGCLASS, "parent"::REGCLASS); + RAISE NOTICE USING MESSAGE = "query"; + EXECUTE "query"; + END LOOP; + END IF; + END LOOP; +END; +$$ +LANGUAGE plpgsql +VOLATILE; diff --git a/dist/pg_full_inherit.control b/dist/pg_full_inherit.control index 3610454..3b6a4ad 100644 --- a/dist/pg_full_inherit.control +++ b/dist/pg_full_inherit.control @@ -1,4 +1,4 @@ comment = 'full inherit' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/pg_full_inherit' relocatable = false diff --git a/event-triggers/add_inherit_constraints.sql b/event-triggers/add_inherit_constraints.sql index aa5a629..6ca6801 100644 --- a/event-triggers/add_inherit_constraints.sql +++ b/event-triggers/add_inherit_constraints.sql @@ -64,7 +64,7 @@ BEGIN -- завершить цикл если constraint пустой EXIT WHEN "constraint" IS NULL; -- имя для ограничения дочерней таблицы - "name" = public.get_child_constraint_name("constraint"."parentname", "constraint"."parentrelid"::REGCLASS::TEXT, "constraint"."childrelid"::REGCLASS::TEXT); + "name" = public.get_child_constraint_name("constraint"."parentname", "constraint"."parentrelid"::REGCLASS, "constraint"."childrelid"::REGCLASS); -- запрос на добавление ограничения name в таблицу childrelid "query" = format('ALTER TABLE %1s ADD CONSTRAINT %2I %3s;', "constraint"."childrelid", "name", "constraint"."parentdef"); RAISE NOTICE USING MESSAGE = format('-- ADD CONSTRAINT %1I TO %2s TABLE FROM %3s TABLE', "name", "constraint"."childrelid", "constraint"."parentrelid"); diff --git a/event-triggers/add_inherit_triggers.sql b/event-triggers/add_inherit_triggers.sql index c279abe..4ef3be9 100644 --- a/event-triggers/add_inherit_triggers.sql +++ b/event-triggers/add_inherit_triggers.sql @@ -57,9 +57,9 @@ BEGIN -- завершить цикл если trigger пустой EXIT WHEN "trigger" IS NULL; -- имя для триггера дочерней таблицы - "name" = public.get_child_trigger_name("trigger"."parentname", "trigger"."parentrelid"::REGCLASS::TEXT, "trigger"."childrelid"::REGCLASS::TEXT); + "name" = public.get_child_trigger_name("trigger"."parentname", "trigger"."parentrelid"::REGCLASS, "trigger"."childrelid"::REGCLASS); -- запрос на добавление триггера в таблицу childrelid - "query" = public.get_child_trigger_def("trigger"."parentdef", "trigger"."parentname", "trigger"."parentrelid"::REGCLASS::TEXT, "trigger"."childrelid"::REGCLASS::TEXT); + "query" = public.get_child_trigger_def("trigger"."parentdef", "trigger"."parentname", "trigger"."parentrelid"::REGCLASS, "trigger"."childrelid"::REGCLASS); RAISE NOTICE USING MESSAGE = format('-- ADD TRIGGER %1I TO %2s TABLE FROM %3s TABLE', "name", "trigger"."childrelid"::REGCLASS, "trigger"."parentrelid"::REGCLASS); RAISE NOTICE USING MESSAGE = "query"; EXECUTE "query"; diff --git a/event-triggers/drop_inherit_constraints.sql b/event-triggers/drop_inherit_constraints.sql index 169a338..4b3f621 100644 --- a/event-triggers/drop_inherit_constraints.sql +++ b/event-triggers/drop_inherit_constraints.sql @@ -33,7 +33,7 @@ BEGIN SELECT inhrelid FROM pg_inherits WHERE inhparent = "parent" LOOP -- имя ограничения дочерней таблице - "name" = public.get_child_constraint_name("name", "parent"::REGCLASS::TEXT, "child"::REGCLASS::TEXT); + "name" = public.get_child_constraint_name("name", "parent"::REGCLASS, "child"::REGCLASS); -- удаление ограничения name из дочерней таблицы "query" = format('ALTER TABLE %1s DROP CONSTRAINT IF EXISTS %2I;', "child"::REGCLASS, "name"); RAISE NOTICE USING MESSAGE = format('-- DROP CONSTRAINT %1I FROM %2s TABLE BASED ON DEPENDENCY ON %3s TABLE', "name", "child"::REGCLASS, "parent"::REGCLASS); diff --git a/event-triggers/drop_inherit_triggers.sql b/event-triggers/drop_inherit_triggers.sql index 75c8686..f8b97a6 100644 --- a/event-triggers/drop_inherit_triggers.sql +++ b/event-triggers/drop_inherit_triggers.sql @@ -33,7 +33,7 @@ BEGIN SELECT inhrelid FROM pg_inherits WHERE inhparent = "parent" LOOP -- имя триггера дочерней таблице - "name" = public.get_child_trigger_name("name", "parent"::REGCLASS::TEXT, "child"::REGCLASS::TEXT); + "name" = public.get_child_trigger_name("name", "parent"::REGCLASS, "child"::REGCLASS); -- удаление триггер name из дочерней таблицы "query" = format('DROP TRIGGER IF EXISTS %1I ON %2s;', "name", "child"::REGCLASS); RAISE NOTICE USING MESSAGE = format('-- DROP TRIGGER %1I FROM %2s TABLE BASED ON DEPENDENCY ON %3s TABLE', "name", "child"::REGCLASS, "parent"::REGCLASS); diff --git a/helpers/def.sql b/helpers/def.sql index 1997969..c112b7b 100644 --- a/helpers/def.sql +++ b/helpers/def.sql @@ -1,9 +1,9 @@ -CREATE FUNCTION public.get_child_trigger_def ("parentdef" TEXT, "parentname" TEXT, "parent" TEXT, "child" TEXT) +CREATE FUNCTION public.get_child_trigger_def ("parentdef" TEXT, "parentname" TEXT, "parent" REGCLASS, "child" REGCLASS) RETURNS TEXT AS $$ BEGIN RETURN regexp_replace ( - replace ("parentdef", "parent", "child"), + replace ("parentdef", public.get_table_name("parent", TRUE), public.get_table_name("child", TRUE)), '(CREATE (CONSTRAINT )?TRIGGER) ' || quote_ident ("parentname"), '\1 ' || quote_ident (public.get_child_trigger_name("parentname", "parent", "child"))); END diff --git a/helpers/get_inherit_triggers.sql b/helpers/get_inherit_triggers.sql index 1821bb6..7cccf2b 100644 --- a/helpers/get_inherit_triggers.sql +++ b/helpers/get_inherit_triggers.sql @@ -32,7 +32,7 @@ BEGIN FROM "pg_inherits" "i" LEFT JOIN "triggers" "pc" ON "i"."inhparent" = "pc"."tgrelid" LEFT JOIN "triggers" "cc" ON "i"."inhrelid" = "cc"."tgrelid" - AND public.get_child_trigger_def("pc"."tgdef", "pc"."tgname":: TEXT, "i"."inhparent"::REGCLASS::TEXT, "i"."inhrelid"::REGCLASS::TEXT) = "cc"."tgdef" + AND public.get_child_trigger_def("pc"."tgdef", "pc"."tgname":: TEXT, "i"."inhparent"::REGCLASS, "i"."inhrelid"::REGCLASS) = "cc"."tgdef" WHERE "pc"."oid" IS NOT NULL OR "cc"."oid" IS NOT NULL; END $$ diff --git a/helpers/get_table_name.sql b/helpers/get_table_name.sql new file mode 100644 index 0000000..1a2d789 --- /dev/null +++ b/helpers/get_table_name.sql @@ -0,0 +1,14 @@ +CREATE FUNCTION public.get_table_name ("relid" REGCLASS, "is_full" BOOLEAN = TRUE) + RETURNS TEXT +AS $$ +BEGIN + RETURN ( + SELECT CASE WHEN "is_full" THEN format('%I.%I', n.nspname, c.relname) + ELSE format('%I', c.relname) + END + FROM pg_class c JOIN pg_namespace n on c.relnamespace = n.oid + WHERE c.oid = "relid"); +END +$$ +LANGUAGE plpgsql +RETURNS NULL ON NULL INPUT; diff --git a/helpers/name.sql b/helpers/name.sql index 6ec4974..f92105e 100644 --- a/helpers/name.sql +++ b/helpers/name.sql @@ -1,15 +1,15 @@ -CREATE FUNCTION public.get_child_constraint_name ("name" TEXT, "parent" TEXT, "child" TEXT) +CREATE FUNCTION public.get_child_constraint_name ("name" TEXT, "parent" REGCLASS, "child" REGCLASS) RETURNS TEXT AS $$ BEGIN - RETURN regexp_replace("name", '^' || "parent", "child"); + RETURN regexp_replace("name", '^' || public.get_table_name("parent", FALSE), public.get_table_name("child", FALSE)); END $$ LANGUAGE plpgsql IMMUTABLE -- функция не может модифицировать базу данных и всегда возвращает один и тот же результат при определённых значениях аргументов RETURNS NULL ON NULL INPUT; -- функция всегда возвращает NULL, получив NULL в одном из аргументов -CREATE FUNCTION public.get_child_trigger_name ("name" TEXT, "parent" TEXT, "child" TEXT) +CREATE FUNCTION public.get_child_trigger_name ("name" TEXT, "parent" REGCLASS, "child" REGCLASS) RETURNS TEXT AS $$ BEGIN