contrib/sepgsql/database.c | 27 +++++ contrib/sepgsql/expected/alter.out | 192 ++++++++++++++++++++++++++++++ contrib/sepgsql/expected/ddl.out | 9 ++ contrib/sepgsql/hooks.c | 66 ++++++++++ contrib/sepgsql/proc.c | 90 +++++++++++++- contrib/sepgsql/relation.c | 103 ++++++++++++++-- contrib/sepgsql/schema.c | 51 ++++++++ contrib/sepgsql/sepgsql.h | 8 ++ contrib/sepgsql/sql/alter.sql | 136 +++++++++++++++++++++ contrib/sepgsql/sql/ddl.sql | 6 + contrib/sepgsql/test_sepgsql | 2 +- doc/src/sgml/sepgsql.sgml | 21 +++- src/backend/catalog/aclchk.c | 9 ++ src/backend/catalog/heap.c | 41 +++++-- src/backend/catalog/index.c | 12 +- src/backend/catalog/objectaccess.c | 45 +++++++ src/backend/catalog/pg_constraint.c | 11 +- src/backend/catalog/pg_db_role_setting.c | 5 +- src/backend/catalog/pg_type.c | 3 + src/backend/commands/alter.c | 9 ++ src/backend/commands/cluster.c | 20 +++- src/backend/commands/collationcmds.c | 1 + src/backend/commands/dbcommands.c | 13 ++ src/backend/commands/event_trigger.c | 3 + src/backend/commands/extension.c | 8 ++ src/backend/commands/foreigncmds.c | 11 ++ src/backend/commands/functioncmds.c | 2 +- src/backend/commands/opclasscmds.c | 6 + src/backend/commands/schemacmds.c | 7 +- src/backend/commands/sequence.c | 4 + src/backend/commands/tablecmds.c | 99 ++++++++++++--- src/backend/commands/tablespace.c | 3 + src/backend/commands/trigger.c | 13 +- src/backend/commands/tsearchcmds.c | 5 + src/backend/commands/typecmds.c | 28 ++++- src/backend/commands/user.c | 5 + src/backend/rewrite/rewriteDefine.c | 2 + src/include/catalog/heap.h | 6 +- src/include/catalog/index.h | 3 +- src/include/catalog/objectaccess.h | 74 ++++++++++++ src/include/catalog/pg_constraint.h | 3 +- src/include/commands/cluster.h | 2 +- src/include/commands/tablecmds.h | 2 +- 43 files changed, 1101 insertions(+), 65 deletions(-) diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c index c15f2d0..942d179 100644 --- a/contrib/sepgsql/database.c +++ b/contrib/sepgsql/database.c @@ -149,6 +149,33 @@ sepgsql_database_drop(Oid databaseId) } /* + * sepgsql_database_post_alter + * + * It checks privileges to alter the supplied database + */ +void +sepgsql_database_setattr(Oid databaseId) +{ + ObjectAddress object; + char *audit_name; + + /* + * check db_database:{setattr} permission + */ + object.classId = DatabaseRelationId; + object.objectId = databaseId; + object.objectSubId = 0; + audit_name = getObjectDescription(&object); + + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__SETATTR, + audit_name, + true); + pfree(audit_name); +} + +/* * sepgsql_database_relabel * * It checks privileges to relabel the supplied database with the `seclabel' diff --git a/contrib/sepgsql/expected/alter.out b/contrib/sepgsql/expected/alter.out new file mode 100644 index 0000000..ef9abb3 --- /dev/null +++ b/contrib/sepgsql/expected/alter.out @@ -0,0 +1,192 @@ +-- +-- Test for various ALTER statements +-- +-- clean-up in case a prior regression run failed +SET client_min_messages TO 'warning'; +DROP DATABASE IF EXISTS regtest_sepgsql_test_database_1; +DROP DATABASE IF EXISTS regtest_sepgsql_test_database; +DROP USER IF EXISTS regtest_sepgsql_test_user; +RESET client_min_messages; +SELECT sepgsql_getcon(); -- confirm client privilege + sepgsql_getcon +------------------------------------------- + unconfined_u:unconfined_r:unconfined_t:s0 +(1 row) + +-- +-- CREATE Objects to be altered (with debug_audit being silent) +-- +CREATE DATABASE regtest_sepgsql_test_database_1; +CREATE USER regtest_sepgsql_test_user; +CREATE SCHEMA regtest_schema_1; +CREATE SCHEMA regtest_schema_2; +GRANT ALL ON SCHEMA regtest_schema_1 TO public; +GRANT ALL ON SCHEMA regtest_schema_2 TO public; +SET search_path = regtest_schema_1, regtest_schema_2, public; +CREATE TABLE regtest_table_1 (a int, b text); +CREATE TABLE regtest_table_2 (c text) inherits (regtest_table_1); +CREATE TABLE regtest_table_3 (x int primary key, y text); +CREATE SEQUENCE regtest_seq_1; +CREATE VIEW regtest_view_1 AS SELECT * FROM regtest_table_1 WHERE a > 0; +CREATE FUNCTION regtest_func_1 (text) RETURNS bool + AS 'BEGIN RETURN true; END' LANGUAGE 'plpgsql'; +-- switch on debug_audit +SET sepgsql.debug_audit = true; +SET client_min_messages = LOG; +-- +-- ALTER xxx OWNER TO +-- +-- XXX: It should take db_xxx:{setattr} permission checks even if +-- owner is not actually changed. +-- +ALTER DATABASE regtest_sepgsql_test_database_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database_1" +ALTER DATABASE regtest_sepgsql_test_database_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database_1" +ALTER SCHEMA regtest_schema_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +ALTER SCHEMA regtest_schema_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +ALTER TABLE regtest_table_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" +ALTER TABLE regtest_table_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" +ALTER SEQUENCE regtest_seq_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq_1" +ALTER SEQUENCE regtest_seq_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq_1" +ALTER VIEW regtest_view_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_1" +ALTER VIEW regtest_view_1 OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_1" +ALTER FUNCTION regtest_func_1(text) OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_1(text)" +ALTER FUNCTION regtest_func_1(text) OWNER TO regtest_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_1(text)" +-- +-- ALTER xxx SET SCHEMA +-- +ALTER TABLE regtest_table_1 SET SCHEMA regtest_schema_2; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" +ALTER SEQUENCE regtest_seq_1 SET SCHEMA regtest_schema_2; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq_1" +ALTER VIEW regtest_view_1 SET SCHEMA regtest_schema_2; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_1" +ALTER FUNCTION regtest_func_1(text) SET SCHEMA regtest_schema_2; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_1(text)" +-- +-- ALTER xxx RENAME TO +-- +ALTER DATABASE regtest_sepgsql_test_database_1 RENAME TO regtest_sepgsql_test_database; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database_1" +ALTER SCHEMA regtest_schema_1 RENAME TO regtest_schema; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_1" +ALTER TABLE regtest_table_1 RENAME TO regtest_table; +LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_1" +ALTER SEQUENCE regtest_seq_1 RENAME TO regtest_seq; +LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq_1" +ALTER VIEW regtest_view_1 RENAME TO regtest_view; +LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_1" +ALTER FUNCTION regtest_func_1(text) RENAME TO regtest_func; +LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func_1(text)" +SET search_path = regtest_schema, regtest_schema_2, public; +-- +-- misc ALTER commands +-- +ALTER DATABASE regtest_sepgsql_test_database CONNECTION LIMIT 999; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database regtest_sepgsql_test_database" +ALTER DATABASE regtest_sepgsql_test_database SET search_path TO regtest_schema, public; -- not supported yet +ALTER TABLE regtest_table ADD COLUMN d float; +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column d" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column d" +ALTER TABLE regtest_table DROP COLUMN d; +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column d" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column d" +ALTER TABLE regtest_table ALTER b SET DEFAULT 'abcd'; -- not supported yet +ALTER TABLE regtest_table ALTER b SET DEFAULT 'XYZ'; -- not supported yet +ALTER TABLE regtest_table ALTER b DROP DEFAULT; -- not supported yet +ALTER TABLE regtest_table ALTER b SET NOT NULL; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column b" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column b" +ALTER TABLE regtest_table ALTER b DROP NOT NULL; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column b" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column b" +ALTER TABLE regtest_table ALTER b SET STATISTICS -1; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column b" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column b" +ALTER TABLE regtest_table ALTER b SET (n_distinct = 999); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column b" +ALTER TABLE regtest_table ALTER b SET STORAGE PLAIN; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column b" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column b" +ALTER TABLE regtest_table ADD CONSTRAINT test_fk FOREIGN KEY (a) REFERENCES regtest_table_3(x); -- not supported +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column a" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_3" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_3 column x" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column a" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_3" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_3 column x" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "regtest_schema_2"."regtest_table" fk LEFT OUTER JOIN ONLY "regtest_schema"."regtest_table_3" pk ON ( pk."x" OPERATOR(pg_catalog.=) fk."a") WHERE pk."x" IS NULL AND (fk."a" IS NOT NULL)" +ALTER TABLE regtest_table ADD CONSTRAINT test_ck CHECK (b like '%abc%') NOT VALID; -- not supported +ALTER TABLE regtest_table VALIDATE CONSTRAINT test_ck; -- not supported +ALTER TABLE regtest_table DROP CONSTRAINT test_ck; -- not supported +CREATE TRIGGER regtest_test_trig BEFORE UPDATE ON regtest_table + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +ALTER TABLE regtest_table DISABLE TRIGGER regtest_test_trig; -- not supported +ALTER TABLE regtest_table ENABLE TRIGGER regtest_test_trig; -- not supported +CREATE RULE regtest_test_rule AS ON INSERT TO regtest_table_3 DO ALSO NOTHING; +ALTER TABLE regtest_table_3 DISABLE RULE regtest_test_rule; -- not supported +ALTER TABLE regtest_table_3 ENABLE RULE regtest_test_rule; -- not supported +ALTER TABLE regtest_table SET WITH OIDS; +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column oid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column oid" +ALTER TABLE regtest_table SET WITHOUT OIDS; +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_2 column oid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column oid" +ALTER TABLE regtest_table SET (fillfactor = 75); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +ALTER TABLE regtest_table RESET (fillfactor); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +ALTER TABLE regtest_table_2 NO INHERIT regtest_table; -- not supported +ALTER TABLE regtest_table_2 INHERIT regtest_table; -- not supported +ALTER TABLE regtest_table SET TABLESPACE pg_default; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +ALTER VIEW regtest_view SET (security_barrier); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view" +ALTER SEQUENCE regtest_seq INCREMENT BY 10 START WITH 1000; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq" +-- +-- clean-up objects +-- +RESET sepgsql.debug_audit; +RESET client_min_messages; +DROP DATABASE regtest_sepgsql_test_database; +DROP SCHEMA regtest_schema CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table regtest_table_2 +drop cascades to table regtest_table_3 +drop cascades to constraint test_fk on table regtest_table +DROP SCHEMA regtest_schema_2 CASCADE; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table regtest_table +drop cascades to sequence regtest_seq +drop cascades to view regtest_view +drop cascades to function regtest_func(text) +DROP USER regtest_sepgsql_test_user; diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out index 1f7ea88..d60c65b 100644 --- a/contrib/sepgsql/expected/ddl.out +++ b/contrib/sepgsql/expected/ddl.out @@ -1,6 +1,11 @@ -- -- Regression Test for DDL of Object Permission Checks -- +-- clean-up in case a prior regression run failed +SET client_min_messages TO 'warning'; +DROP DATABASE IF EXISTS regtest_sepgsql_test_database; +DROP USER IF EXISTS regtest_sepgsql_test_user; +RESET client_min_messages; -- confirm required permissions using audit messages SELECT sepgsql_getcon(); -- confirm client privilege sepgsql_getcon @@ -36,6 +41,7 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column y" LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq" ALTER TABLE regtest_table ADD COLUMN z int; LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column z" CREATE TABLE regtest_table_2 (a int) WITH OIDS; @@ -61,6 +67,7 @@ CREATE SEQUENCE regtest_seq; LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq" CREATE TYPE regtest_comptype AS (a int, b text); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" CREATE FUNCTION regtest_func(text,int[]) RETURNS bool LANGUAGE plpgsql AS 'BEGIN RAISE NOTICE ''regtest_func => %'', $1; RETURN true; END'; LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" @@ -86,6 +93,7 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_3 column ctid" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_3 column x" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_3 column y" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_3_y_seq" CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y; LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view_2" @@ -118,6 +126,7 @@ CREATE INDEX regtest_index_tbl4_z ON regtest_table_4(z); LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" ALTER TABLE regtest_table_4 ALTER COLUMN y TYPE float; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_4 column y" DROP INDEX regtest_index_tbl4_y; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table_4" diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index ab55d6e..5ba3dd9 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -188,6 +188,72 @@ sepgsql_object_access(ObjectAccessType access, } break; + case OAT_PREP_ALTER: + { + ObjectAccessPrepAlter *pa_arg = arg; + + switch (classId) + { + case RelationRelationId: + Assert(subId == 0); + sepgsql_relation_set_tablespace(objectId, + pa_arg->tablespaceId); + break; + default: + /* Ignore unsupported object classes */ + break; + } + } + break; + + case OAT_POST_ALTER: + { + ObjectAccessPostAlter *pa_arg = arg; + bool is_internal = (!pa_arg ? false : pa_arg->is_internal); + + switch (classId) + { + case DatabaseRelationId: + Assert(!is_internal); + sepgsql_database_setattr(objectId); + break; + + case NamespaceRelationId: + Assert(!is_internal); + sepgsql_schema_setattr(objectId); + break; + + case RelationRelationId: + if (subId == 0) + { + /* + * A case when we don't want to apply permission + * check is that relation is internally altered + * without user's intention. E.g, no need to + * check on toast table/index to be renamed at + * end of the table rewrites. + */ + if (is_internal) + break; + + sepgsql_relation_setattr(objectId); + } + else + sepgsql_attribute_setattr(objectId, subId); + break; + + case ProcedureRelationId: + Assert(!is_internal); + sepgsql_proc_setattr(objectId); + break; + + default: + /* Ignore unsupported object classes */ + break; + } + } + break; + default: elog(ERROR, "unexpected object access type: %d", (int) access); break; diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c index fbd358a..6124f00 100644 --- a/contrib/sepgsql/proc.c +++ b/contrib/sepgsql/proc.c @@ -23,6 +23,7 @@ #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/syscache.h" #include "utils/tqual.h" #include "sepgsql.h" @@ -43,6 +44,7 @@ sepgsql_proc_post_create(Oid functionId) char *scontext; char *tcontext; char *ncontext; + uint32 required; int i; StringInfoData audit_name; ObjectAddress object; @@ -96,7 +98,7 @@ sepgsql_proc_post_create(Oid functionId) SEPG_CLASS_DB_PROCEDURE); /* - * check db_procedure:{create} permission + * check db_procedure:{create (install)} permission */ initStringInfo(&audit_name); appendStringInfo(&audit_name, "function %s(", NameStr(proForm->proname)); @@ -110,9 +112,13 @@ sepgsql_proc_post_create(Oid functionId) } appendStringInfoChar(&audit_name, ')'); + required = SEPG_DB_PROCEDURE__CREATE; + if (proForm->proleakproof) + required |= SEPG_DB_PROCEDURE__INSTALL; + sepgsql_avc_check_perms_label(ncontext, SEPG_CLASS_DB_PROCEDURE, - SEPG_DB_PROCEDURE__CREATE, + required, audit_name.data, true); @@ -214,3 +220,83 @@ sepgsql_proc_relabel(Oid functionId, const char *seclabel) true); pfree(audit_name); } + +/* + * sepgsql_proc_setattr + * + * It checks privileges to alter the supplied function. + */ +void +sepgsql_proc_setattr(Oid functionId) +{ + Relation rel; + ScanKeyData skey; + SysScanDesc sscan; + HeapTuple oldtup; + HeapTuple newtup; + Form_pg_proc oldform; + Form_pg_proc newform; + uint32 required; + ObjectAddress object; + char *audit_name; + + /* + * Fetch newer catalog + */ + rel = heap_open(ProcedureRelationId, AccessShareLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(functionId)); + + sscan = systable_beginscan(rel, ProcedureOidIndexId, true, + SnapshotSelf, 1, &skey); + newtup = systable_getnext(sscan); + if (!HeapTupleIsValid(newtup)) + elog(ERROR, "catalog lookup failed for function %u", functionId); + newform = (Form_pg_proc) GETSTRUCT(newtup); + + /* + * Fetch older catalog + */ + oldtup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); + if (!HeapTupleIsValid(oldtup)) + elog(ERROR, "cache lookup failed for function %u", functionId); + oldform = (Form_pg_proc) GETSTRUCT(oldtup); + + /* + * Does this ALTER command takes operation to namespace? + */ + if (newform->pronamespace != oldform->pronamespace) + { + sepgsql_schema_remove_name(oldform->pronamespace); + sepgsql_schema_add_name(oldform->pronamespace); + } + if (strcmp(NameStr(newform->proname), NameStr(oldform->proname)) != 0) + sepgsql_schema_rename(oldform->pronamespace); + + /* + * check db_procedure:{setattr (install)} permission + */ + required = SEPG_DB_PROCEDURE__SETATTR; + if (!oldform->proleakproof && newform->proleakproof) + required |= SEPG_DB_PROCEDURE__INSTALL; + + object.classId = ProcedureRelationId; + object.objectId = functionId; + object.objectSubId = 0; + audit_name = getObjectDescription(&object); + + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_PROCEDURE, + required, + audit_name, + true); + /* cleanups */ + pfree(audit_name); + + ReleaseSysCache(oldtup); + systable_endscan(sscan); + heap_close(rel, AccessShareLock); +} diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c index 783f330..7ed4da9 100644 --- a/contrib/sepgsql/relation.c +++ b/contrib/sepgsql/relation.c @@ -191,6 +191,36 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, } /* + * sepgsql_attribute_setattr + * + * It checks privileges to alter the supplied column. + */ +void +sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum) +{ + ObjectAddress object; + char *audit_name; + + if (get_rel_relkind(relOid) != RELKIND_RELATION) + return; + + /* + * check db_column:{setattr} permission + */ + object.classId = RelationRelationId; + object.objectId = relOid; + object.objectSubId = attnum; + audit_name = getObjectDescription(&object); + + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__SETATTR, + audit_name, + true); + pfree(audit_name); +} + +/* * sepgsql_relation_post_create * * The post creation hook of relation/attribute @@ -529,6 +559,13 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel) void sepgsql_relation_setattr(Oid relOid) { + Relation rel; + ScanKeyData skey; + SysScanDesc sscan; + HeapTuple oldtup; + HeapTuple newtup; + Form_pg_class oldform; + Form_pg_class newform; ObjectAddress object; char *audit_name; uint16_t tclass; @@ -553,26 +590,78 @@ sepgsql_relation_setattr(Oid relOid) return; } - object.classId = RelationRelationId; - object.objectId = relOid; - object.objectSubId = 0; - audit_name = getObjectDescription(&object); + /* + * Fetch newer catalog + */ + rel = heap_open(RelationRelationId, AccessShareLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relOid)); + + sscan = systable_beginscan(rel, ClassOidIndexId, true, + SnapshotSelf, 1, &skey); + + newtup = systable_getnext(sscan); + if (!HeapTupleIsValid(newtup)) + elog(ERROR, "catalog lookup failed for relation %u", relOid); + newform = (Form_pg_class) GETSTRUCT(newtup); + + /* + * Fetch older catalog + */ + oldtup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); + if (!HeapTupleIsValid(oldtup)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + oldform = (Form_pg_class) GETSTRUCT(oldtup); /* - * XXX - we should add checks related to namespace stuff, when - * object_access_hook get support for ALTER statement. Right now, there is - * no invocation path on ALTER ... RENAME TO / SET SCHEMA. + * Does this ALTER command takes operation to namespace? */ + if (newform->relnamespace != oldform->relnamespace) + { + sepgsql_schema_remove_name(oldform->relnamespace); + sepgsql_schema_add_name(newform->relnamespace); + } + if (strcmp(NameStr(newform->relname), NameStr(oldform->relname)) != 0) + sepgsql_schema_rename(oldform->relnamespace); /* * check db_xxx:{setattr} permission */ + object.classId = RelationRelationId; + object.objectId = relOid; + object.objectSubId = 0; + audit_name = getObjectDescription(&object); + sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__SETATTR, audit_name, true); pfree(audit_name); + + ReleaseSysCache(oldtup); + systable_endscan(sscan); + heap_close(rel, AccessShareLock); +} + +/* + * sepgsql_relation_set_tablespace + * + * It checks privileges to set tablespace of the supplied relation + */ +void +sepgsql_relation_set_tablespace(Oid relOid, Oid tablespaceId) +{ + /* check db_xxx:{setattr} permission */ + sepgsql_relation_setattr(relOid); + + /* + * XXX - In the future version, we also check db_tuple:{use} + * permission onto relevant tuple in the pg_tablespace catalog. + */ } /* diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c index e063e39..240a6d9 100644 --- a/contrib/sepgsql/schema.c +++ b/contrib/sepgsql/schema.c @@ -162,3 +162,54 @@ sepgsql_schema_relabel(Oid namespaceId, const char *seclabel) true); pfree(audit_name); } + +/* + * sepgsql_schema_check_perms + * + * utility routine to check db_schema:{xxx} permissions + */ +static void +check_schema_perms(Oid namespaceId, uint32 required) +{ + ObjectAddress object; + char *audit_name; + + object.classId = NamespaceRelationId; + object.objectId = namespaceId; + object.objectSubId = 0; + audit_name = getObjectDescription(&object); + + sepgsql_avc_check_perms(&object, + SEPG_CLASS_DB_SCHEMA, + required, + audit_name, + true); + pfree(audit_name); +} + +/* db_schema:{setattr} permission */ +void +sepgsql_schema_setattr(Oid namespaceId) +{ + check_schema_perms(namespaceId, SEPG_DB_SCHEMA__SETATTR); +} + +void +sepgsql_schema_add_name(Oid namespaceId) +{ + check_schema_perms(namespaceId, SEPG_DB_SCHEMA__ADD_NAME); +} + +void +sepgsql_schema_remove_name(Oid namespaceId) +{ + check_schema_perms(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME); +} + +void +sepgsql_schema_rename(Oid namespaceId) +{ + check_schema_perms(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME); +} diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index b6dcb86..94ecbb0 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -293,6 +293,7 @@ extern void sepgsql_database_post_create(Oid databaseId, const char *dtemplate); extern void sepgsql_database_drop(Oid databaseId); extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel); +extern void sepgsql_database_setattr(Oid databaseId); /* * schema.c @@ -300,6 +301,10 @@ extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel); extern void sepgsql_schema_post_create(Oid namespaceId); extern void sepgsql_schema_drop(Oid namespaceId); extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel); +extern void sepgsql_schema_setattr(Oid namespaceId); +extern void sepgsql_schema_add_name(Oid namespaceId); +extern void sepgsql_schema_remove_name(Oid namespaceId); +extern void sepgsql_schema_rename(Oid namespaceId); /* * relation.c @@ -308,10 +313,12 @@ extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum); extern void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum); extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, const char *seclabel); +extern void sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum); extern void sepgsql_relation_post_create(Oid relOid); extern void sepgsql_relation_drop(Oid relOid); extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel); extern void sepgsql_relation_setattr(Oid relOid); +extern void sepgsql_relation_set_tablespace(Oid relOid, Oid tablespaceId); /* * proc.c @@ -319,5 +326,6 @@ extern void sepgsql_relation_setattr(Oid relOid); extern void sepgsql_proc_post_create(Oid functionId); extern void sepgsql_proc_drop(Oid functionId); extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel); +extern void sepgsql_proc_setattr(Oid functionId); #endif /* SEPGSQL_H */ diff --git a/contrib/sepgsql/sql/alter.sql b/contrib/sepgsql/sql/alter.sql new file mode 100644 index 0000000..4bded7e --- /dev/null +++ b/contrib/sepgsql/sql/alter.sql @@ -0,0 +1,136 @@ +-- +-- Test for various ALTER statements +-- + +-- clean-up in case a prior regression run failed +SET client_min_messages TO 'warning'; +DROP DATABASE IF EXISTS regtest_sepgsql_test_database_1; +DROP DATABASE IF EXISTS regtest_sepgsql_test_database; +DROP USER IF EXISTS regtest_sepgsql_test_user; +RESET client_min_messages; + +-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0 + +-- +-- CREATE Objects to be altered (with debug_audit being silent) +-- +CREATE DATABASE regtest_sepgsql_test_database_1; + +CREATE USER regtest_sepgsql_test_user; + +CREATE SCHEMA regtest_schema_1; +CREATE SCHEMA regtest_schema_2; + +GRANT ALL ON SCHEMA regtest_schema_1 TO public; +GRANT ALL ON SCHEMA regtest_schema_2 TO public; + +SET search_path = regtest_schema_1, regtest_schema_2, public; + +CREATE TABLE regtest_table_1 (a int, b text); + +CREATE TABLE regtest_table_2 (c text) inherits (regtest_table_1); + +CREATE TABLE regtest_table_3 (x int primary key, y text); + +CREATE SEQUENCE regtest_seq_1; + +CREATE VIEW regtest_view_1 AS SELECT * FROM regtest_table_1 WHERE a > 0; + +CREATE FUNCTION regtest_func_1 (text) RETURNS bool + AS 'BEGIN RETURN true; END' LANGUAGE 'plpgsql'; + +-- switch on debug_audit +SET sepgsql.debug_audit = true; +SET client_min_messages = LOG; + +-- +-- ALTER xxx OWNER TO +-- +-- XXX: It should take db_xxx:{setattr} permission checks even if +-- owner is not actually changed. +-- +ALTER DATABASE regtest_sepgsql_test_database_1 OWNER TO regtest_sepgsql_test_user; +ALTER DATABASE regtest_sepgsql_test_database_1 OWNER TO regtest_sepgsql_test_user; +ALTER SCHEMA regtest_schema_1 OWNER TO regtest_sepgsql_test_user; +ALTER SCHEMA regtest_schema_1 OWNER TO regtest_sepgsql_test_user; +ALTER TABLE regtest_table_1 OWNER TO regtest_sepgsql_test_user; +ALTER TABLE regtest_table_1 OWNER TO regtest_sepgsql_test_user; +ALTER SEQUENCE regtest_seq_1 OWNER TO regtest_sepgsql_test_user; +ALTER SEQUENCE regtest_seq_1 OWNER TO regtest_sepgsql_test_user; +ALTER VIEW regtest_view_1 OWNER TO regtest_sepgsql_test_user; +ALTER VIEW regtest_view_1 OWNER TO regtest_sepgsql_test_user; +ALTER FUNCTION regtest_func_1(text) OWNER TO regtest_sepgsql_test_user; +ALTER FUNCTION regtest_func_1(text) OWNER TO regtest_sepgsql_test_user; + +-- +-- ALTER xxx SET SCHEMA +-- +ALTER TABLE regtest_table_1 SET SCHEMA regtest_schema_2; +ALTER SEQUENCE regtest_seq_1 SET SCHEMA regtest_schema_2; +ALTER VIEW regtest_view_1 SET SCHEMA regtest_schema_2; +ALTER FUNCTION regtest_func_1(text) SET SCHEMA regtest_schema_2; + +-- +-- ALTER xxx RENAME TO +-- +ALTER DATABASE regtest_sepgsql_test_database_1 RENAME TO regtest_sepgsql_test_database; +ALTER SCHEMA regtest_schema_1 RENAME TO regtest_schema; +ALTER TABLE regtest_table_1 RENAME TO regtest_table; +ALTER SEQUENCE regtest_seq_1 RENAME TO regtest_seq; +ALTER VIEW regtest_view_1 RENAME TO regtest_view; +ALTER FUNCTION regtest_func_1(text) RENAME TO regtest_func; + +SET search_path = regtest_schema, regtest_schema_2, public; + +-- +-- misc ALTER commands +-- +ALTER DATABASE regtest_sepgsql_test_database CONNECTION LIMIT 999; +ALTER DATABASE regtest_sepgsql_test_database SET search_path TO regtest_schema, public; -- not supported yet + +ALTER TABLE regtest_table ADD COLUMN d float; +ALTER TABLE regtest_table DROP COLUMN d; +ALTER TABLE regtest_table ALTER b SET DEFAULT 'abcd'; -- not supported yet +ALTER TABLE regtest_table ALTER b SET DEFAULT 'XYZ'; -- not supported yet +ALTER TABLE regtest_table ALTER b DROP DEFAULT; -- not supported yet +ALTER TABLE regtest_table ALTER b SET NOT NULL; +ALTER TABLE regtest_table ALTER b DROP NOT NULL; +ALTER TABLE regtest_table ALTER b SET STATISTICS -1; +ALTER TABLE regtest_table ALTER b SET (n_distinct = 999); +ALTER TABLE regtest_table ALTER b SET STORAGE PLAIN; +ALTER TABLE regtest_table ADD CONSTRAINT test_fk FOREIGN KEY (a) REFERENCES regtest_table_3(x); -- not supported +ALTER TABLE regtest_table ADD CONSTRAINT test_ck CHECK (b like '%abc%') NOT VALID; -- not supported +ALTER TABLE regtest_table VALIDATE CONSTRAINT test_ck; -- not supported +ALTER TABLE regtest_table DROP CONSTRAINT test_ck; -- not supported + +CREATE TRIGGER regtest_test_trig BEFORE UPDATE ON regtest_table + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); + +ALTER TABLE regtest_table DISABLE TRIGGER regtest_test_trig; -- not supported +ALTER TABLE regtest_table ENABLE TRIGGER regtest_test_trig; -- not supported + +CREATE RULE regtest_test_rule AS ON INSERT TO regtest_table_3 DO ALSO NOTHING; +ALTER TABLE regtest_table_3 DISABLE RULE regtest_test_rule; -- not supported +ALTER TABLE regtest_table_3 ENABLE RULE regtest_test_rule; -- not supported + +ALTER TABLE regtest_table SET WITH OIDS; +ALTER TABLE regtest_table SET WITHOUT OIDS; +ALTER TABLE regtest_table SET (fillfactor = 75); +ALTER TABLE regtest_table RESET (fillfactor); +ALTER TABLE regtest_table_2 NO INHERIT regtest_table; -- not supported +ALTER TABLE regtest_table_2 INHERIT regtest_table; -- not supported +ALTER TABLE regtest_table SET TABLESPACE pg_default; + +ALTER VIEW regtest_view SET (security_barrier); + +ALTER SEQUENCE regtest_seq INCREMENT BY 10 START WITH 1000; + +-- +-- clean-up objects +-- +RESET sepgsql.debug_audit; +RESET client_min_messages; +DROP DATABASE regtest_sepgsql_test_database; +DROP SCHEMA regtest_schema CASCADE; +DROP SCHEMA regtest_schema_2 CASCADE; +DROP USER regtest_sepgsql_test_user; diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql index 5afe1ba..c91c4cf 100644 --- a/contrib/sepgsql/sql/ddl.sql +++ b/contrib/sepgsql/sql/ddl.sql @@ -2,6 +2,12 @@ -- Regression Test for DDL of Object Permission Checks -- +-- clean-up in case a prior regression run failed +SET client_min_messages TO 'warning'; +DROP DATABASE IF EXISTS regtest_sepgsql_test_database; +DROP USER IF EXISTS regtest_sepgsql_test_user; +RESET client_min_messages; + -- confirm required permissions using audit messages -- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0 SET sepgsql.debug_audit = true; diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql index 473004f..eac14ff 100755 --- a/contrib/sepgsql/test_sepgsql +++ b/contrib/sepgsql/test_sepgsql @@ -259,6 +259,6 @@ echo "found ${NUM}" echo echo "============== running sepgsql regression tests ==============" -make REGRESS="label dml ddl misc" REGRESS_OPTS="--launcher ./launcher" installcheck +make REGRESS="label dml ddl alter misc" REGRESS_OPTS="--launcher ./launcher" installcheck # exit with the exit code provided by "make" diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml index 522aa8b..a9141ff 100644 --- a/doc/src/sgml/sepgsql.sgml +++ b/doc/src/sgml/sepgsql.sgml @@ -438,21 +438,36 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100; On creation of objects within a particular schema (tables, views, sequences and procedures), add_name will be also checked on the schema, not only create on the new object itself. + On , install permission + will be checked if leakproof attribute was given, not only + create on the new function. When DROP command is executed, drop will be checked on the object being removed for each object types. Permissions - will not be checked for objects dropped indirectly via CASCADE. + will also be checked for objects dropped indirectly via CASCADE. Deletion of objects contained within a particular schema (tables, views, sequences and procedures) additionally requires remove_name on the schema. + When ALTER command is executed, setattr will be + checked on the object being modified for each object types. + In addition, add_name and remove_name permission + will be checked towards relevant schema when we try to rename or set + new schema on the altered object. + A few additional checks are applied depending on object types. + On , install permission + will be checked if leakproof attribute was turned on, not + only setattr on the new function. + + + When objects that are subsidiary of other objects (such as a table's indexes - or triggers) are created or dropped, setattr permission will be - checked on the main object, instead of the subsidiary object itself. + or triggers) are created, dropped or altered, setattr permission + will be checked on the main object, instead of the subsidiary object itself. diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 4e4c7af..07788d9 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -25,6 +25,7 @@ #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" +#include "catalog/objectaccess.h" #include "catalog/pg_authid.h" #include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" @@ -1288,6 +1289,14 @@ SetDefaultACL(InternalDefaultACL *iacls) iacls->roleid, noldmembers, oldmembers, nnewmembers, newmembers); + + /* Post create or alter hook of this default ACL */ + if (isNew) + InvokeObjectPostCreateHook(DefaultAclRelationId, + HeapTupleGetOid(newtuple), 0); + else + InvokeObjectPostAlterHook(DefaultAclRelationId, + HeapTupleGetOid(newtuple), 0); } if (HeapTupleIsValid(tuple)) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 54de9a7..f482656 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -95,8 +95,9 @@ static Oid AddNewRelationType(const char *typeName, static void RelationRemoveInheritance(Oid relid); static void StoreRelCheck(Relation rel, char *ccname, Node *expr, bool is_validated, bool is_local, int inhcount, - bool is_no_inherit); -static void StoreConstraints(Relation rel, List *cooked_constraints); + bool is_no_inherit, bool is_internal); +static void StoreConstraints(Relation rel, List *cooked_constraints, + bool is_internal); static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr, bool allow_merge, bool is_local, bool is_no_inherit); @@ -1286,7 +1287,7 @@ heap_create_with_catalog(const char *relname, * entry, so the relation must be valid and self-consistent at this point. * In particular, there are not yet constraints and defaults anywhere. */ - StoreConstraints(new_rel_desc, cooked_constraints); + StoreConstraints(new_rel_desc, cooked_constraints, is_internal); /* * If there's a special on-commit action, remember it @@ -1799,7 +1800,8 @@ heap_drop_with_catalog(Oid relid) * Store a default expression for column attnum of relation rel. */ void -StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr) +StoreAttrDefault(Relation rel, AttrNumber attnum, + Node *expr, bool is_internal) { char *adbin; char *adsrc; @@ -1891,6 +1893,17 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr) * Record dependencies on objects used in the expression, too. */ recordDependencyOnExpr(&defobject, expr, NIL, DEPENDENCY_NORMAL); + + /* + * Post creation hook of this attribute defaults + * + * XXX - Note that ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is + * implemented with a couple of deletion/creation of the attribute's + * default entry, so the callee should check existence of an older + * version of this entry if needed to distinguish. + */ + InvokeObjectPostCreateHookArg(AttrDefaultRelationId, + RelationGetRelid(rel), attnum, is_internal); } /* @@ -1902,7 +1915,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr) static void StoreRelCheck(Relation rel, char *ccname, Node *expr, bool is_validated, bool is_local, int inhcount, - bool is_no_inherit) + bool is_no_inherit, bool is_internal) { char *ccbin; char *ccsrc; @@ -1986,7 +1999,8 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr, ccsrc, /* Source form of check constraint */ is_local, /* conislocal */ inhcount, /* coninhcount */ - is_no_inherit); /* connoinherit */ + is_no_inherit, /* connoinherit */ + is_internal); /* internally constructed? */ pfree(ccbin); pfree(ccsrc); @@ -2001,7 +2015,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr, * and StoreRelCheck (see AddRelationNewConstraints()). */ static void -StoreConstraints(Relation rel, List *cooked_constraints) +StoreConstraints(Relation rel, List *cooked_constraints, bool is_internal) { int numchecks = 0; ListCell *lc; @@ -2023,11 +2037,12 @@ StoreConstraints(Relation rel, List *cooked_constraints) switch (con->contype) { case CONSTR_DEFAULT: - StoreAttrDefault(rel, con->attnum, con->expr); + StoreAttrDefault(rel, con->attnum, con->expr, is_internal); break; case CONSTR_CHECK: StoreRelCheck(rel, con->name, con->expr, !con->skip_validation, - con->is_local, con->inhcount, con->is_no_inherit); + con->is_local, con->inhcount, + con->is_no_inherit, is_internal); numchecks++; break; default: @@ -2053,6 +2068,7 @@ StoreConstraints(Relation rel, List *cooked_constraints) * newConstraints: list of Constraint nodes * allow_merge: TRUE if check constraints may be merged with existing ones * is_local: TRUE if definition is local, FALSE if it's inherited + * is_internal: TRUE if constraint is constructed unless user's intention * * All entries in newColDefaults will be processed. Entries in newConstraints * will be processed only if they are CONSTR_CHECK type. @@ -2070,7 +2086,8 @@ AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, - bool is_local) + bool is_local, + bool is_internal) { List *cookedConstraints = NIL; TupleDesc tupleDesc; @@ -2133,7 +2150,7 @@ AddRelationNewConstraints(Relation rel, (IsA(expr, Const) &&((Const *) expr)->constisnull)) continue; - StoreAttrDefault(rel, colDef->attnum, expr); + StoreAttrDefault(rel, colDef->attnum, expr, is_internal); cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint)); cooked->contype = CONSTR_DEFAULT; @@ -2259,7 +2276,7 @@ AddRelationNewConstraints(Relation rel, * OK, store it. */ StoreRelCheck(rel, ccname, expr, !cdef->skip_validation, is_local, - is_local ? 0 : 1, cdef->is_no_inherit); + is_local ? 0 : 1, cdef->is_no_inherit, is_internal); numchecks++; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index b1091ac..bc20725 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -932,7 +932,8 @@ index_create(Relation heapRelation, false, /* already marked primary */ false, /* pg_index entry is OK */ false, /* no old dependencies */ - allow_system_table_mods); + allow_system_table_mods, + is_internal); } else { @@ -1106,6 +1107,7 @@ index_create(Relation heapRelation, * remove_old_dependencies: if true, remove existing dependencies of index * on table's columns * allow_system_table_mods: allow table to be a system catalog + * is_internal: index is constructed due to internal process */ void index_constraint_create(Relation heapRelation, @@ -1118,7 +1120,8 @@ index_constraint_create(Relation heapRelation, bool mark_as_primary, bool update_pgindex, bool remove_old_dependencies, - bool allow_system_table_mods) + bool allow_system_table_mods, + bool is_internal) { Oid namespaceId = RelationGetNamespace(heapRelation); ObjectAddress myself, @@ -1183,7 +1186,8 @@ index_constraint_create(Relation heapRelation, NULL, true, /* islocal */ 0, /* inhcount */ - true); /* noinherit */ + true, /* noinherit */ + is_internal); /* * Register the index as internally dependent on the constraint. @@ -1292,6 +1296,8 @@ index_constraint_create(Relation heapRelation, { simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); CatalogUpdateIndexes(pg_index, indexTuple); + InvokeObjectPostAlterHookArg(IndexRelationId, indexRelationId, 0, + InvalidOid, is_internal); } heap_freetuple(indexTuple); diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c index 79c1136..cf48bef 100644 --- a/src/backend/catalog/objectaccess.c +++ b/src/backend/catalog/objectaccess.c @@ -61,3 +61,48 @@ RunObjectDropHook(Oid classId, Oid objectId, int subId, classId, objectId, subId, (void *) &drop_arg); } + +/* + * RunObjectPrepAlterHook + * + * It is entrypoint of OAT_PREP_ALTER event + */ +void +RunObjectPrepAlterHook(Oid classId, Oid objectId, int subId, + Oid tablespaceId) +{ + ObjectAccessPrepAlter pa_arg; + + /* XXX - should be checked at caller side */ + Assert(object_access_hook != NULL); + + memset(&pa_arg, 0, sizeof(ObjectAccessPrepAlter)); + pa_arg.tablespaceId = tablespaceId; + + (*object_access_hook)(OAT_PREP_ALTER, + classId, objectId, subId, + (void *) &pa_arg); +} + +/* + * RunObjectPostAlterHook + * + * It is entrypoint of OAT_POST_ALTER event + */ +void +RunObjectPostAlterHook(Oid classId, Oid objectId, int subId, + Oid auxiliaryId, bool is_internal) +{ + ObjectAccessPostAlter pa_arg; + + /* XXX - should be checked at caller side */ + Assert(object_access_hook != NULL); + + memset(&pa_arg, 0, sizeof(ObjectAccessPostAlter)); + pa_arg.auxiliary_id = auxiliaryId; + pa_arg.is_internal = is_internal; + + (*object_access_hook)(OAT_POST_ALTER, + classId, objectId, subId, + (void *) &pa_arg); +} diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 1e6d947..de5bc81 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -68,7 +68,8 @@ CreateConstraintEntry(const char *constraintName, const char *conSrc, bool conIsLocal, int conInhCount, - bool conNoInherit) + bool conNoInherit, + bool is_internal) { Relation conDesc; Oid conOid; @@ -367,7 +368,8 @@ CreateConstraintEntry(const char *constraintName, } /* Post creation hook for new constraint */ - InvokeObjectPostCreateHook(ConstraintRelationId, conOid, 0); + InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0, + is_internal); return conOid; } @@ -665,6 +667,9 @@ RenameConstraintById(Oid conId, const char *newname) /* update the system catalog indexes */ CatalogUpdateIndexes(conDesc, tuple); + /* Post alter hook for this constraint */ + InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0); + heap_freetuple(tuple); heap_close(conDesc, RowExclusiveLock); } @@ -736,6 +741,8 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, * changeDependencyFor(). */ } + /* Post alter hook for the constraint */ + InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0); add_exact_object_address(&thisobj, objsMoved); } diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c index 616980c..9a0ead7 100644 --- a/src/backend/catalog/pg_db_role_setting.c +++ b/src/backend/catalog/pg_db_role_setting.c @@ -14,6 +14,7 @@ #include "access/heapam.h" #include "access/htup_details.h" #include "catalog/indexing.h" +#include "catalog/objectaccess.h" #include "catalog/pg_db_role_setting.h" #include "utils/fmgroids.h" #include "utils/rel.h" @@ -159,7 +160,9 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) /* Update indexes */ CatalogUpdateIndexes(rel, newtuple); } - + /* Post alter hook for this database-role-settings */ + InvokeObjectPostAlterHookArg(DbRoleSettingRelationId, + databaseid, 0, roleid, false); systable_endscan(scan); /* Close pg_db_role_setting, but keep lock till commit */ diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index cf0ebcc..8c756eb 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -712,6 +712,9 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) /* update the system catalog indexes */ CatalogUpdateIndexes(pg_type_desc, tuple); + /* Post alter hook for this type */ + InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); + heap_freetuple(tuple); heap_close(pg_type_desc, RowExclusiveLock); diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 6f08210..5b257ff 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -19,6 +19,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_namespace.h" @@ -177,6 +178,9 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name) simple_heap_update(rel, &oldtup->t_self, newtup); CatalogUpdateIndexes(rel, newtup); + /* Post alter hook of the object */ + InvokeObjectPostAlterHook(classId, objectId, 0); + /* Release memory */ pfree(values); pfree(nulls); @@ -560,6 +564,9 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) changeDependencyFor(classId, objid, NamespaceRelationId, oldNspOid, nspOid); + /* Post alter hook for the object */ + InvokeObjectPostAlterHook(classId, objid, 0); + return oldNspOid; } @@ -837,4 +844,6 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId) pfree(nulls); pfree(replaces); } + /* Post alter hook for the object */ + InvokeObjectPostAlterHook(classId, objectId, 0); } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index c3deb56..65abd20 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -26,6 +26,7 @@ #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/toasting.h" #include "commands/cluster.h" #include "commands/tablecmds.h" @@ -465,7 +466,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMOD * otherwise concurrent executions of RelationGetIndexList could miss indexes. */ void -mark_index_clustered(Relation rel, Oid indexOid) +mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) { HeapTuple indexTuple; Form_pg_index indexForm; @@ -525,6 +526,9 @@ mark_index_clustered(Relation rel, Oid indexOid) simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); CatalogUpdateIndexes(pg_index, indexTuple); } + /* Post object alter hook of this index */ + InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0, + InvalidOid, is_internal); heap_freetuple(indexTuple); } @@ -552,7 +556,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, /* Mark the correct index as clustered */ if (OidIsValid(indexOid)) - mark_index_clustered(OldHeap, indexOid); + mark_index_clustered(OldHeap, indexOid, true); /* Remember if it's a system catalog */ is_system_catalog = IsSystemRelation(OldHeap); @@ -1257,6 +1261,14 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, } /* + * Post alter hook of these relations, but with is_internal=true + */ + InvokeObjectPostAlterHookArg(RelationRelationId, r1, 0, + InvalidOid, true); + InvokeObjectPostAlterHookArg(RelationRelationId, r2, 0, + InvalidOid, true); + + /* * If we have toast tables associated with the relations being swapped, * deal with them too. */ @@ -1495,13 +1507,13 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", OIDOldHeap); RenameRelationInternal(newrel->rd_rel->reltoastrelid, - NewToastName); + NewToastName, true); /* ... and its index too */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); RenameRelationInternal(toastidx, - NewToastName); + NewToastName, true); } relation_close(newrel, NoLock); } diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index 458b573..9eecd2b 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -20,6 +20,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_collation.h" #include "catalog/pg_collation_fn.h" #include "commands/alter.h" diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 09c06ed..8695a52 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -965,6 +965,9 @@ RenameDatabase(const char *oldname, const char *newname) simple_heap_update(rel, &newtup->t_self, newtup); CatalogUpdateIndexes(rel, newtup); + /* Post alter hook for this database */ + InvokeObjectPostAlterHook(DatabaseRelationId, db_id, 0); + /* * Close pg_database, but keep lock till commit. */ @@ -1200,6 +1203,10 @@ movedb(const char *dbname, const char *tblspcname) /* Update indexes */ CatalogUpdateIndexes(pgdbrel, newtuple); + /* Post alter hook for this database */ + InvokeObjectPostAlterHook(DatabaseRelationId, + HeapTupleGetOid(newtuple), 0); + systable_endscan(sysscan); /* @@ -1394,6 +1401,10 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) /* Update indexes */ CatalogUpdateIndexes(rel, newtuple); + /* Post alter hook for this database */ + InvokeObjectPostAlterHook(DatabaseRelationId, + HeapTupleGetOid(newtuple), 0); + systable_endscan(scan); /* Close pg_database, but keep lock till commit */ @@ -1526,6 +1537,8 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) changeDependencyOnOwner(DatabaseRelationId, HeapTupleGetOid(tuple), newOwnerId); } + /* Post alter hook for the database */ + InvokeObjectPostAlterHook(DatabaseRelationId, HeapTupleGetOid(tuple), 0); systable_endscan(scan); diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 97dde84..ad32c9b 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -491,6 +491,9 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) changeDependencyOnOwner(EventTriggerRelationId, HeapTupleGetOid(tup), newOwnerId); + /* Post alter hook for the event trigger */ + InvokeObjectPostAlterHook(EventTriggerRelationId, + HeapTupleGetOid(tup), 0); } /* diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index e45de83..d067fec 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -2360,6 +2360,9 @@ AlterExtensionNamespace(List *names, const char *newschema) /* update dependencies to point to the new schema */ changeDependencyFor(ExtensionRelationId, extensionOid, NamespaceRelationId, oldNspOid, nspOid); + + /* Post alter hook for the extension */ + InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0); } /* @@ -2640,6 +2643,9 @@ ApplyExtensionUpdates(Oid extensionOid, recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); } + /* Post alter hook for this extension */ + InvokeObjectPostAlterHook(ExtensionRelationId, extensionOid, 0); + /* * Finally, execute the update script file */ @@ -2745,6 +2751,8 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) DEPENDENCY_EXTENSION) != 1) elog(ERROR, "unexpected number of extension dependency records"); } + /* Post alter hook of this extension */ + InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0); /* * If get_object_address() opened the relation for us, we close it to keep diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index e2f86d2..5cb6c2f 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -241,6 +241,9 @@ AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI HeapTupleGetOid(tup), newOwnerId); } + /* Post alter hook for the FDW */ + InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, + HeapTupleGetOid(tup), 0); } /* @@ -344,6 +347,9 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup), newOwnerId); } + /* Post alter hook for the foreign server */ + InvokeObjectPostAlterHook(ForeignServerRelationId, + HeapTupleGetOid(tup), 0); } /* @@ -751,6 +757,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } + /* Post alter hook of this FDW */ + InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0); heap_close(rel, RowExclusiveLock); } @@ -978,6 +986,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) simple_heap_update(rel, &tp->t_self, tp); CatalogUpdateIndexes(rel, tp); + /* Post alter hook of this foreign server */ + InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0); + heap_freetuple(tp); heap_close(rel, RowExclusiveLock); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 26a806b..91f9fdc 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1222,7 +1222,7 @@ AlterFunction(AlterFunctionStmt *stmt) /* Do the update */ simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); - + InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0); heap_close(rel, NoLock); heap_freetuple(tup); } diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index c8e0e75..35f10b5 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -1370,6 +1370,9 @@ storeOperators(List *opfamilyname, Oid amoid, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* Post create hook of this access method operator */ + InvokeObjectPostCreateHook(AccessMethodOperatorRelationId, + entryoid, 0); } heap_close(rel, RowExclusiveLock); @@ -1469,6 +1472,9 @@ storeProcedures(List *opfamilyname, Oid amoid, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } + /* Post create hook of access method procedure */ + InvokeObjectPostCreateHook(AccessMethodProcedureRelationId, + entryoid, 0); } heap_close(rel, RowExclusiveLock); diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index e69c86b..9d4d75e 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -21,6 +21,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_namespace.h" #include "commands/dbcommands.h" #include "commands/schemacmds.h" @@ -235,6 +236,8 @@ RenameSchema(const char *oldname, const char *newname) simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); + InvokeObjectPostAlterHook(NamespaceRelationId, HeapTupleGetOid(tup), 0); + heap_close(rel, NoLock); heap_freetuple(tup); } @@ -363,5 +366,7 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup), newOwnerId); } - + /* Post alter hook for the namespace */ + InvokeObjectPostAlterHook(NamespaceRelationId, + HeapTupleGetOid(tup), 0); } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 634ce3f..3829003 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -19,6 +19,7 @@ #include "access/xlogutils.h" #include "catalog/dependency.h" #include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "commands/sequence.h" @@ -482,6 +483,9 @@ AlterSequence(AlterSeqStmt *stmt) if (owned_by) process_owned_by(seqrel, owned_by); + /* Post alter hook of this sequence */ + InvokeObjectPostAlterHook(RelationRelationId, relid, 0); + relation_close(seqrel, NoLock); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index d516e2a..90b00fe 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -663,7 +663,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) */ if (rawDefaults || stmt->constraints) AddRelationNewConstraints(rel, rawDefaults, stmt->constraints, - true, true); + true, true, false); /* * Clean up. We keep lock on new relation (although it shouldn't be @@ -1955,6 +1955,11 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid, recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL); + /* Post creation hook of this inheritance */ + InvokeObjectPostAlterHookArg(InheritsRelationId, + relationId, 0, + parentOid, false); + /* * Mark the parent as having subclasses. */ @@ -2213,6 +2218,9 @@ renameatt_internal(Oid myrelid, /* keep system catalog indexes current */ CatalogUpdateIndexes(attrelation, atttup); + /* Post alter hook for this attribute */ + InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum); + heap_freetuple(atttup); heap_close(attrelation, RowExclusiveLock); @@ -2357,7 +2365,7 @@ rename_constraint_internal(Oid myrelid, || con->contype == CONSTRAINT_UNIQUE || con->contype == CONSTRAINT_EXCLUSION)) /* rename the index; this renames the constraint as well */ - RenameRelationInternal(con->conindid, newconname); + RenameRelationInternal(con->conindid, newconname, false); else RenameConstraintById(constraintOid, newconname); @@ -2433,7 +2441,7 @@ RenameRelation(RenameStmt *stmt) } /* Do the work */ - RenameRelationInternal(relid, stmt->newname); + RenameRelationInternal(relid, stmt->newname, false); } /* @@ -2446,7 +2454,7 @@ RenameRelation(RenameStmt *stmt) * sequence, AFAIK there's no need for it to be there. */ void -RenameRelationInternal(Oid myrelid, const char *newrelname) +RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) { Relation targetrelation; Relation relrelation; /* for RELATION relation */ @@ -2488,6 +2496,10 @@ RenameRelationInternal(Oid myrelid, const char *newrelname) /* keep the system catalog indexes current */ CatalogUpdateIndexes(relrelation, reltup); + /* Post alter hook for this relation */ + InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0, + InvalidOid, is_internal); + heap_freetuple(reltup); heap_close(relrelation, RowExclusiveLock); @@ -3309,7 +3321,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, */ break; case AT_SetTableSpace: /* SET TABLESPACE */ - + InvokeObjectPrepAlterHookArg(RelationRelationId, + RelationGetRelid(rel), 0, + tab->newTableSpace); /* * Nothing to do here; Phase 3 does the work */ @@ -4489,7 +4503,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, * This function is intended for CREATE TABLE, so it processes a * _list_ of defaults, but we just do one. */ - AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true); + AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, + false, true, false); /* Make the additional catalog changes visible */ CommandCounterIncrement(); @@ -4826,6 +4841,8 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) /* keep the system catalog indexes current */ CatalogUpdateIndexes(attr_rel, tuple); } + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), attnum); heap_close(attr_rel, RowExclusiveLock); } @@ -4878,6 +4895,8 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, /* Tell Phase 3 it needs to test the constraint */ tab->new_notnull = true; } + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), attnum); heap_close(attr_rel, RowExclusiveLock); } @@ -4933,7 +4952,8 @@ ATExecColumnDefault(Relation rel, const char *colName, * This function is intended for CREATE TABLE, so it processes a * _list_ of defaults, but we just do one. */ - AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, false, true); + AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, + false, true, false); } } @@ -5017,6 +5037,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE /* keep system catalog indexes current */ CatalogUpdateIndexes(attrelation, tuple); + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), + attrtuple->attnum); heap_freetuple(tuple); heap_close(attrelation, RowExclusiveLock); @@ -5074,13 +5097,17 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, repl_repl[Anum_pg_attribute_attoptions - 1] = true; newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation), repl_val, repl_null, repl_repl); - ReleaseSysCache(tuple); /* Update system catalog. */ simple_heap_update(attrelation, &newtuple->t_self, newtuple); CatalogUpdateIndexes(attrelation, newtuple); + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), + attrtuple->attnum); heap_freetuple(newtuple); + ReleaseSysCache(tuple); + heap_close(attrelation, RowExclusiveLock); } @@ -5150,6 +5177,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc /* keep system catalog indexes current */ CatalogUpdateIndexes(attrelation, tuple); + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), + attrtuple->attnum); heap_freetuple(tuple); heap_close(attrelation, RowExclusiveLock); @@ -5464,7 +5494,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, ereport(NOTICE, (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", indexName, constraintName))); - RenameRelationInternal(index_oid, constraintName); + RenameRelationInternal(index_oid, constraintName, false); } /* Extra checks needed if making primary key */ @@ -5488,7 +5518,8 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, stmt->primary, true, /* update pg_index */ true, /* remove old dependencies */ - allowSystemTableMods); + allowSystemTableMods, + false); /* is_internal */ index_close(indexRel, NoLock); } @@ -5600,7 +5631,8 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, newcons = AddRelationNewConstraints(rel, NIL, list_make1(copyObject(constr)), recursing, /* allow_merge */ - !recursing); /* is_local */ + !recursing, /* is_local */ + is_readd); /* is_internal */ /* Add each to-be-validated constraint to Phase 3's queue */ foreach(lcon, newcons) @@ -6050,7 +6082,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, NULL, true, /* islocal */ 0, /* inhcount */ - true); /* isnoinherit */ + true, /* isnoinherit */ + false); /* is_internal */ /* * Create the triggers that will enforce the constraint. @@ -6232,6 +6265,8 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse, copy_con->convalidated = true; simple_heap_update(conrel, ©Tuple->t_self, copyTuple); CatalogUpdateIndexes(conrel, copyTuple); + InvokeObjectPostAlterHook(ConstraintRelationId, + HeapTupleGetOid(tuple), 0); heap_freetuple(copyTuple); } @@ -7659,6 +7694,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, */ RemoveStatistics(RelationGetRelid(rel), attnum); + /* Post alter hook of this column */ + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), attnum); + /* * Update the default, if present, by brute force --- remove and re-add * the default. Probably unsafe to take shortcuts, since the new version @@ -7678,7 +7717,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true, true); - StoreAttrDefault(rel, attnum, defaultexpr); + StoreAttrDefault(rel, attnum, defaultexpr, true); } /* Cleanup */ @@ -7769,10 +7808,13 @@ ATExecAlterColumnGenericOptions(Relation rel, newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel), repl_val, repl_null, repl_repl); - ReleaseSysCache(tuple); simple_heap_update(attrel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(attrel, newtuple); + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), + atttableform->attnum); + ReleaseSysCache(tuple); heap_close(attrel, RowExclusiveLock); @@ -8244,6 +8286,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode); } } + /* Post alter hook of this relation */ + InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0); ReleaseSysCache(tuple); heap_close(class_rel, RowExclusiveLock); @@ -8406,7 +8450,7 @@ ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode) check_index_is_clusterable(rel, indexOid, false, lockmode); /* And do the work */ - mark_index_clustered(rel, indexOid); + mark_index_clustered(rel, indexOid, false); } /* @@ -8418,7 +8462,7 @@ ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode) static void ATExecDropCluster(Relation rel, LOCKMODE lockmode) { - mark_index_clustered(rel, InvalidOid); + mark_index_clustered(rel, InvalidOid, false); } /* @@ -8538,6 +8582,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, CatalogUpdateIndexes(pgclass, newtuple); + InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0); + heap_freetuple(newtuple); ReleaseSysCache(tuple); @@ -8595,6 +8641,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, CatalogUpdateIndexes(pgclass, newtuple); + InvokeObjectPostAlterHookArg(RelationRelationId, + RelationGetRelid(toastrel), 0, + InvalidOid, true); + heap_freetuple(newtuple); ReleaseSysCache(tuple); @@ -8732,7 +8782,9 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) rd_rel->relfilenode = newrelfilenode; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); - + InvokeObjectPostAlterHookArg(RelationRelationId, + RelationGetRelid(rel), 0, + InvalidOid, true); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); @@ -9421,6 +9473,11 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode) RelationRelationId, RelationGetRelid(parent_rel)); + /* Post alter hook of this inherits */ + InvokeObjectPostAlterHookArg(InheritsRelationId, + RelationGetRelid(rel), 0, + RelationGetRelid(parent_rel), false); + /* keep our lock on the parent relation until commit */ heap_close(parent_rel, NoLock); } @@ -9603,6 +9660,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid; simple_heap_update(relationRelation, &classtuple->t_self, classtuple); CatalogUpdateIndexes(relationRelation, classtuple); + InvokeObjectPostAlterHook(RelationRelationId, relid, 0); heap_freetuple(classtuple); heap_close(relationRelation, RowExclusiveLock); @@ -9643,6 +9701,7 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode) ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid; simple_heap_update(relationRelation, &tuple->t_self, tuple); CatalogUpdateIndexes(relationRelation, tuple); + InvokeObjectPostAlterHook(RelationRelationId, relid, 0); heap_freetuple(tuple); heap_close(relationRelation, RowExclusiveLock); } @@ -9712,6 +9771,9 @@ ATExecGenericOptions(Relation rel, List *options) simple_heap_update(ftrel, &tuple->t_self, tuple); CatalogUpdateIndexes(ftrel, tuple); + InvokeObjectPostAlterHook(ForeignTableRelationId, + RelationGetRelid(rel), 0); + heap_close(ftrel, RowExclusiveLock); heap_freetuple(tuple); @@ -9865,6 +9927,9 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid, NameStr(classForm->relname)); add_exact_object_address(&thisobj, objsMoved); + + /* Post alter hook for this relation */ + InvokeObjectPostAlterHook(RelationRelationId, relOid, 0); } heap_freetuple(classTup); diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index e794ecf..ade5d75 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -862,6 +862,8 @@ RenameTableSpace(const char *oldname, const char *newname) /* OK, update the entry */ AlterObjectRename_internal(rel, tableSpaceId, newname); + InvokeObjectPostAlterHook(TableSpaceRelationId, tableSpaceId, 0); + heap_close(rel, NoLock); } @@ -925,6 +927,7 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) /* Update system catalog. */ simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); + InvokeObjectPostAlterHook(TableSpaceRelationId, HeapTupleGetOid(tup), 0); heap_freetuple(newtuple); /* Conclude heap scan. */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 4a369a4..311edef 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -445,7 +445,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, NULL, true, /* islocal */ 0, /* inhcount */ - true); /* isnoinherit */ + true, /* isnoinherit */ + isInternal); /* is_internal */ } /* @@ -741,7 +742,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, DEPENDENCY_NORMAL); /* Post creation hook for new trigger */ - InvokeObjectPostCreateHook(TriggerRelationId, trigoid, 0); + InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0, + isInternal); /* Keep lock on target rel until end of xact */ heap_close(rel, NoLock); @@ -1273,6 +1275,10 @@ renametrig(RenameStmt *stmt) /* keep system catalog indexes current */ CatalogUpdateIndexes(tgrel, tuple); + /* Post alter hook for this trigger */ + InvokeObjectPostAlterHook(TriggerRelationId, + HeapTupleGetOid(tuple), 0); + /* * Invalidate relation's relcache entry so that other backends (and * this one too!) are sent SI message to make them rebuild relcache @@ -1386,6 +1392,9 @@ EnableDisableTrigger(Relation rel, const char *tgname, changed = true; } + /* Post alter hook for this trigger */ + InvokeObjectPostAlterHook(TriggerRelationId, + HeapTupleGetOid(tuple), 0); } systable_endscan(tgscan); diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index 64e9885..61342d2 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -608,6 +608,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) CatalogUpdateIndexes(rel, newtup); + InvokeObjectPostAlterHook(TSDictionaryRelationId, dictId, 0); + /* * NOTE: because we only support altering the options, not the template, * there is no need to update dependencies. This might have to change if @@ -1171,6 +1173,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) /* Update dependencies */ makeConfigurationDependencies(tup, true, relMap); + InvokeObjectPostAlterHook(TSConfigMapRelationId, + HeapTupleGetOid(tup), 0); + heap_close(relMap, RowExclusiveLock); ReleaseSysCache(tup); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 36de6d7..b9fff13 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -39,6 +39,7 @@ #include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/indexing.h" +#include "catalog/objectaccess.h" #include "catalog/pg_authid.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" @@ -1210,6 +1211,9 @@ AlterEnum(AlterEnumStmt *stmt, bool isTopLevel) stmt->newValNeighbor, stmt->newValIsAfter, stmt->skipIfExists); + /* Post alter hook of this enum type */ + InvokeObjectPostAlterHook(TypeRelationId, enum_type_oid, 0); + ReleaseSysCache(tup); } @@ -2183,7 +2187,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) typTup->typcollation, defaultExpr, true); /* Rebuild is true */ - + /* Post alter hook of this domain */ + InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0); /* Clean up */ heap_close(rel, NoLock); heap_freetuple(newtuple); @@ -2280,6 +2285,9 @@ AlterDomainNotNull(List *names, bool notNull) CatalogUpdateIndexes(typrel, tup); + /* Post alter hook of this domain */ + InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0); + /* Clean up */ heap_freetuple(tup); heap_close(typrel, RowExclusiveLock); @@ -2561,6 +2569,8 @@ AlterDomainValidateConstraint(List *names, char *constrName) copy_con->convalidated = true; simple_heap_update(conrel, ©Tuple->t_self, copyTuple); CatalogUpdateIndexes(conrel, copyTuple); + InvokeObjectPostAlterHook(ConstraintRelationId, + HeapTupleGetOid(copyTuple), 0); heap_freetuple(copyTuple); systable_endscan(scan); @@ -2959,7 +2969,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, ccsrc, /* Source form of check constraint */ true, /* is local */ 0, /* inhcount */ - false); /* connoinherit */ + false, /* connoinherit */ + false); /* is_internal */ /* * Return the compiled constraint expression so the calling routine can @@ -3154,7 +3165,7 @@ RenameType(RenameStmt *stmt) * RenameRelationInternal will call RenameTypeInternal automatically. */ if (typTup->typtype == TYPTYPE_COMPOSITE) - RenameRelationInternal(typTup->typrelid, newTypeName); + RenameRelationInternal(typTup->typrelid, newTypeName, false); else RenameTypeInternal(typeOid, newTypeName, typTup->typnamespace); @@ -3275,6 +3286,9 @@ AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) /* Update owner dependency reference */ changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); + /* Post alter hook of this type */ + InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); + /* If it has an array type, update that too */ if (OidIsValid(typTup->typarray)) AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false); @@ -3295,6 +3309,8 @@ AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) * * hasDependEntry should be TRUE if type is expected to have a pg_shdepend * entry (ie, it's not a table rowtype nor an array type). + * is_primary_ops should be TRUE if this function is invoked with user's + * direct operation (e.g, shdepReassignOwned). Elsewhere, */ void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, @@ -3328,6 +3344,9 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, if (OidIsValid(typTup->typarray)) AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false); + /* Post alter hook for the type */ + InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); + /* Clean up */ heap_close(rel, RowExclusiveLock); } @@ -3513,6 +3532,9 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, elog(ERROR, "failed to change schema dependency for type %s", format_type_be(typeOid)); + /* Post alter hook for this type */ + InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); + heap_freetuple(tup); heap_close(rel, RowExclusiveLock); diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index c1b7b93..2b17675 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -775,6 +775,9 @@ AlterRole(AlterRoleStmt *stmt) /* Update indexes */ CatalogUpdateIndexes(pg_authid_rel, new_tuple); + /* Post alter hook of this role */ + InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0); + ReleaseSysCache(tuple); heap_freetuple(new_tuple); @@ -1128,6 +1131,8 @@ RenameRole(const char *oldname, const char *newname) CatalogUpdateIndexes(rel, newtuple); + InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0); + ReleaseSysCache(oldtuple); /* diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 17e3513..6f7e87d 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -733,6 +733,8 @@ EnableDisableRule(Relation rel, const char *rulename, changed = true; } + InvokeObjectPostAlterHook(RewriteRelationId, + HeapTupleGetOid(ruletup), 0); heap_freetuple(ruletup); heap_close(pg_rewrite_desc, RowExclusiveLock); diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index a35829b..5d335e0 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -95,9 +95,11 @@ extern List *AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, - bool is_local); + bool is_local, + bool is_internal); -extern void StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr); +extern void StoreAttrDefault(Relation rel, AttrNumber attnum, + Node *expr, bool is_internal); extern Node *cookDefault(ParseState *pstate, Node *raw_default, diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index b96099f..225ee73 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -72,7 +72,8 @@ extern void index_constraint_create(Relation heapRelation, bool mark_as_primary, bool update_pgindex, bool remove_old_dependencies, - bool allow_system_table_mods); + bool allow_system_table_mods, + bool is_internal); extern void index_drop(Oid indexId, bool concurrent); diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h index 3e080ba..aac090a 100644 --- a/src/include/catalog/objectaccess.h +++ b/src/include/catalog/objectaccess.h @@ -22,12 +22,23 @@ * OAT_DROP should be invoked just before deletion of objects; typically * deleteOneObject(). Its arguments are packed within ObjectAccessDrop. * + * OAT_PREP_ALTER should be invoked prior to actual works of alter commends, + * but limited on some cases when OAT_POST_ALTER does not work well. Right + * now, all the cases of this events are ALTER TABLE ... SET TABLESPACE. + * + * OAT_POST_ALTER should be invoked just after the object is altered. + * The command-counter is not incremented prior to invocation of this hook, + * extension can reference two different version of system catalog using + * SnapshotNow and SnapshotSelf, to identify which field was altered. + * * Other types may be added in the future. */ typedef enum ObjectAccessType { OAT_POST_CREATE, OAT_DROP, + OAT_PREP_ALTER, + OAT_POST_ALTER, } ObjectAccessType; /* @@ -56,6 +67,43 @@ typedef struct } ObjectAccessDrop; /* + * Arguments of OAT_PREP_ALTER event + */ +typedef struct +{ + /* + * It is used to inform Oid of new tablespace on ALTER TABLE ... SET + * TABLESPACE command. Its system catalog is updated after whole the + * table rewrite even if it is very large, so we put prep-alter hook + * prior to actual works. + */ + Oid tablespaceId; +} ObjectAccessPrepAlter; + + +/* + * Arguments of OAT_POST_ALTER event + */ +typedef struct +{ + /* + * This identifier is used when system catalog takes two IDs + * to identify a particular tuple of the catalog. + * It is only used when the caller want to identify an entry + * of pg_inherits, pg_db_role_setting or pg_user_mapping. + * Elsewhere, InvalidOid should be set. + */ + Oid auxiliary_id; + + /* + * This flag informs extensions whether the context of this alter + * is invoked by user's operations, or not. E.g, it shall be dealt + * as internal stuff on indexing due to type changes. + */ + bool is_internal; +} ObjectAccessPostAlter; + +/* * Hook, and a macro to invoke it. */ typedef void (*object_access_hook_type) (ObjectAccessType access, @@ -70,6 +118,10 @@ extern void RunObjectPostCreateHook(Oid classId, Oid objectId, int subId, bool is_internal); extern void RunObjectDropHook(Oid classId, Oid objectId, int subId, int dropflags); +extern void RunObjectPrepAlterHook(Oid classId, Oid objectId, int subId, + Oid tablespaceId); +extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId, + Oid auxiliaryId, bool is_internal); #define InvokeObjectPostCreateHook(classId,objectId,subId) \ InvokeObjectPostCreateHookArg((classId),(objectId),(subId),false) @@ -89,4 +141,26 @@ extern void RunObjectDropHook(Oid classId, Oid objectId, int subId, (dropflags)); \ } while(0) +#define InvokeObjectPrepAlterHook(classId,objectId,subId) \ + InvokeObjectPrepAlterHookArg((classId),(objectId),(subId), \ + InvalidOid) +#define InvokeObjectPrepAlterHookArg(classId,objectId,subId, \ + tablespaceId) \ + do { \ + if (object_access_hook) \ + RunObjectPrepAlterHook((classId),(objectId),(subId), \ + (tablespaceId)); \ + } while(0) + +#define InvokeObjectPostAlterHook(classId,objectId,subId) \ + InvokeObjectPostAlterHookArg((classId),(objectId),(subId), \ + InvalidOid,false) +#define InvokeObjectPostAlterHookArg(classId,objectId,subId, \ + auxiliaryId,is_internal) \ + do { \ + if (object_access_hook) \ + RunObjectPostAlterHook((classId),(objectId),(subId), \ + (auxiliaryId),(is_internal)); \ + } while(0) + #endif /* OBJECTACCESS_H */ diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index e4e9c40..9c88e51 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -232,7 +232,8 @@ extern Oid CreateConstraintEntry(const char *constraintName, const char *conSrc, bool conIsLocal, int conInhCount, - bool conNoInherit); + bool conNoInherit, + bool is_internal); extern void RemoveConstraintById(Oid conId); extern void RenameConstraintById(Oid conId, const char *newname); diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index 21b2a58..490b3ba 100644 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -23,7 +23,7 @@ extern void cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, int freeze_min_age, int freeze_table_age); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); -extern void mark_index_clustered(Relation rel, Oid indexOid); +extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 4f32062..2ddcbc6 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -58,7 +58,7 @@ extern void RenameConstraint(RenameStmt *stmt); extern void RenameRelation(RenameStmt *stmt); extern void RenameRelationInternal(Oid myrelid, - const char *newrelname); + const char *newrelname, bool is_internal); extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation,