diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index f4df6df10fa71c50194f0d0c749b95c11ba375f6..00a9aea556543574952ee2018a47c3a78defbb33 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -27,7 +27,10 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_am.h" #include "catalog/pg_authid.h" +#include "catalog/pg_cast.h" #include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" @@ -50,6 +53,9 @@ #include "catalog/pg_type.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" +#include "catalog/pg_ts_parser.h" +#include "catalog/pg_ts_template.h" +#include "catalog/pg_transform.h" #include "commands/dbcommands.h" #include "commands/event_trigger.h" #include "commands/extension.h" @@ -131,6 +137,8 @@ static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnu Oid roleid, AclMode mask, AclMaskHow how); static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl); +static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, + Acl *new_acl); #ifdef ACLDEBUG @@ -5288,10 +5296,367 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid) } /* - * Record initial ACL for an extension object + * Record initial privileges for the top-level object passed in. * - * This will perform a wholesale replacement of the entire ACL for the object - * passed in, therefore be sure to pass in the complete new ACL to use. + * For the object passed in, this will record its ACL (if any) and the ACLs of + * any sub-objects (eg: columns) into pg_init_privs. + * + * Any new kinds of objects which have ACLs associated with them and can be + * added to an extension should be added to the if-else tree below. + */ +void +recordExtObjInitPriv(Oid objoid, Oid classoid) +{ + /* + * pg_class / pg_attribute + * + * If this is a relation then we need to see if there are any sub-objects + * (eg: columns) for it and, if so, be sure to call + * recordExtensionInitPrivWorker() for each one. + */ + if (classoid == RelationRelationId) + { + Form_pg_class pg_class_tuple; + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", objoid); + pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); + + /* Indexes don't have permissions */ + if (pg_class_tuple->relkind == RELKIND_INDEX) + return; + + /* Composite types don't have permissions either */ + if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) + return; + + /* + * If this isn't a sequence, index, or composite type then it's + * possibly going to have columns associated with it that might have + * ACLs. + */ + if (pg_class_tuple->relkind != RELKIND_SEQUENCE) + { + AttrNumber curr_att; + AttrNumber nattrs = pg_class_tuple->relnatts; + + for (curr_att = 1; curr_att <= nattrs; curr_att++) + { + HeapTuple attTuple; + Datum attaclDatum; + + attTuple = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(objoid), + Int16GetDatum(curr_att)); + + if (!HeapTupleIsValid(attTuple)) + continue; + + /* ignore dropped columns */ + if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped) + { + ReleaseSysCache(attTuple); + continue; + } + + attaclDatum = SysCacheGetAttr(ATTNUM, attTuple, + Anum_pg_attribute_attacl, + &isNull); + + /* no need to do anything for a NULL ACL */ + if (isNull) + { + ReleaseSysCache(attTuple); + continue; + } + + recordExtensionInitPrivWorker(objoid, classoid, curr_att, + DatumGetAclP(attaclDatum)); + + ReleaseSysCache(attTuple); + } + } + + aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, + &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + /* pg_foreign_data_wrapper */ + else if (classoid == ForeignDataWrapperRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for foreign data wrapper %u", + objoid); + + aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple, + Anum_pg_foreign_data_wrapper_fdwacl, + &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + /* pg_foreign_server */ + else if (classoid == ForeignServerRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for foreign data wrapper %u", + objoid); + + aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple, + Anum_pg_foreign_server_srvacl, + &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + /* pg_language */ + else if (classoid == LanguageRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for language %u", objoid); + + aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl, + &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + /* pg_largeobject_metadata */ + else if (classoid == LargeObjectMetadataRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + ScanKeyData entry[1]; + SysScanDesc scan; + Relation relation; + + relation = heap_open(LargeObjectMetadataRelationId, RowExclusiveLock); + + /* There's no syscache for pg_largeobject_metadata */ + ScanKeyInit(&entry[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objoid)); + + scan = systable_beginscan(relation, + LargeObjectMetadataOidIndexId, true, + NULL, 1, entry); + + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for large object %u", objoid); + + aclDatum = heap_getattr(tuple, + Anum_pg_largeobject_metadata_lomacl, + RelationGetDescr(relation), &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + systable_endscan(scan); + } + /* pg_namespace */ + else if (classoid == NamespaceRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", objoid); + + aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, + Anum_pg_namespace_nspacl, &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + /* pg_proc */ + else if (classoid == ProcedureRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", objoid); + + aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl, + &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + /* pg_type */ + else if (classoid == TypeRelationId) + { + Datum aclDatum; + bool isNull; + HeapTuple tuple; + + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", objoid); + + aclDatum = SysCacheGetAttr(TYPEOID, tuple, Anum_pg_type_typacl, + &isNull); + + /* Add the record, if any, for the top-level object */ + if (!isNull) + recordExtensionInitPrivWorker(objoid, classoid, 0, + DatumGetAclP(aclDatum)); + + ReleaseSysCache(tuple); + } + else if (classoid == AccessMethodRelationId || + classoid == AggregateRelationId || + classoid == CastRelationId || + classoid == CollationRelationId || + classoid == ConversionRelationId || + classoid == EventTriggerRelationId || + classoid == OperatorRelationId || + classoid == OperatorClassRelationId || + classoid == OperatorFamilyRelationId || + classoid == NamespaceRelationId || + classoid == TSConfigRelationId || + classoid == TSDictionaryRelationId || + classoid == TSParserRelationId || + classoid == TSTemplateRelationId || + classoid == TransformRelationId + ) + { + /* no ACL for these object types, so do nothing. */ + } + + /* + * complain if we are given a class OID for a class that extensions don't + * support or that we don't recognize. + */ + else + { + elog(ERROR, "unrecognized or unsupported class OID: %u", classoid); + } +} + +/* + * For the object passed in, remove its ACL and the ACLs of any object subIds + * from pg_init_privs (via recordExtensionInitPrivWorker()). + */ +void +removeExtObjInitPriv(Oid objoid, Oid classoid) +{ + /* + * If this is a relation then we need to see if there are any sub-objects + * (eg: columns) for it and, if so, be sure to call + * recordExtensionInitPrivWorker() for each one. + */ + if (classoid == RelationRelationId) + { + Form_pg_class pg_class_tuple; + HeapTuple tuple; + + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", objoid); + pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple); + + /* Indexes don't have permissions */ + if (pg_class_tuple->relkind == RELKIND_INDEX) + return; + + /* Composite types don't have permissions either */ + if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE) + return; + + /* + * If this isn't a sequence, index, or composite type then it's + * possibly going to have columns associated with it that might have + * ACLs. + */ + if (pg_class_tuple->relkind != RELKIND_SEQUENCE) + { + AttrNumber curr_att; + AttrNumber nattrs = pg_class_tuple->relnatts; + + for (curr_att = 1; curr_att <= nattrs; curr_att++) + { + HeapTuple attTuple; + + attTuple = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(objoid), + Int16GetDatum(curr_att)); + + if (!HeapTupleIsValid(attTuple)) + continue; + + /* when removing, remove all entires, even dropped columns */ + + recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL); + + ReleaseSysCache(attTuple); + } + } + + ReleaseSysCache(tuple); + } + + /* Remove the record, if any, for the top-level object */ + recordExtensionInitPrivWorker(objoid, classoid, 0, NULL); +} + +/* + * Record initial ACL for an extension object * * Can be called at any time, we check if 'creating_extension' is set and, if * not, exit immediately. @@ -5310,12 +5675,6 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid) static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) { - Relation relation; - ScanKeyData key[3]; - SysScanDesc scan; - HeapTuple tuple; - HeapTuple oldtuple; - /* * Generally, we only record the initial privileges when an extension is * being created, but because we don't actually use CREATE EXTENSION @@ -5327,6 +5686,30 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) if (!creating_extension && !binary_upgrade_record_init_privs) return; + recordExtensionInitPrivWorker(objoid, classoid, objsubid, new_acl); +} + +/* + * Record initial ACL for an extension object, worker. + * + * This will perform a wholesale replacement of the entire ACL for the object + * passed in, therefore be sure to pass in the complete new ACL to use. + * + * Generally speaking, do *not* use this function directly but instead use + * recordExtensionInitPriv(), which checks if 'creating_extension' is set. + * This function does *not* check if 'creating_extension' is set as it is also + * used when an object is added to or removed from an extension via ALTER + * EXTENSION ... ADD/DROP. + */ +static void +recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) +{ + Relation relation; + ScanKeyData key[3]; + SysScanDesc scan; + HeapTuple tuple; + HeapTuple oldtuple; + relation = heap_open(InitPrivsRelationId, RowExclusiveLock); ScanKeyInit(&key[0], @@ -5379,28 +5762,37 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) } else { - /* No entry found, so add it. */ Datum values[Natts_pg_init_privs]; bool nulls[Natts_pg_init_privs]; - MemSet(nulls, false, sizeof(nulls)); + /* + * Only add a new entry if the new ACL is non-NULL. + * + * If we are passed in a NULL ACL and no entry exists, we can just + * fall through and do nothing. + */ + if (new_acl) + { + /* No entry found, so add it. */ + MemSet(nulls, false, sizeof(nulls)); - values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid); - values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid); - values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid); + values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid); + values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid); + values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid); - /* This function only handles initial privileges of extensions */ - values[Anum_pg_init_privs_privtype - 1] = - CharGetDatum(INITPRIVS_EXTENSION); + /* This function only handles initial privileges of extensions */ + values[Anum_pg_init_privs_privtype - 1] = + CharGetDatum(INITPRIVS_EXTENSION); - values[Anum_pg_init_privs_privs - 1] = PointerGetDatum(new_acl); + values[Anum_pg_init_privs_privs - 1] = PointerGetDatum(new_acl); - tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); + tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); - simple_heap_insert(relation, tuple); + simple_heap_insert(relation, tuple); - /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, tuple); + /* keep the catalog indexes up to date */ + CatalogUpdateIndexes(relation, tuple); + } } systable_endscan(scan); diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 9680d986a014e4ee39c67ddaeec775616743d5a8..f23c6977cb27ae0d98fe372d848db806ce76d094 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -52,6 +52,7 @@ #include "nodes/makefuncs.h" #include "storage/fd.h" #include "tcop/utility.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -3240,6 +3241,16 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt, * OK, add the dependency. */ recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION); + + /* + * Also record the initial ACL on the object, if any. + * + * Note that this will handle the object's ACLs, as well as any ACLs + * on object subIds. (In other words, when the object is a table, + * this will record the table's ACL and the ACLs for the columns on + * the table, if any). + */ + recordExtObjInitPriv(object.objectId, object.classId); } else { @@ -3267,6 +3278,16 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt, */ if (object.classId == RelationRelationId) extension_config_remove(extension.objectId, object.objectId); + + /* + * Remove all the initial ACLs, if any. + * + * Note that this will remove the object's ACLs, as well as any ACLs + * on object subIds. (In other words, when the object is a table, + * this will remove the table's ACL and the ACLs for the columns on + * the table, if any). + */ + removeExtObjInitPriv(object.objectId, object.classId); } InvokeObjectPostAlterHook(ExtensionRelationId, extension.objectId, 0); diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 686141b5f9abb0d842da71edb14ac7b6ec67ea59..0d118525c9cb3dd1df43df82ff4c3ed24c10c4c1 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -300,6 +300,10 @@ extern void aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind, extern void aclcheck_error_type(AclResult aclerr, Oid typeOid); +extern void recordExtObjInitPriv(Oid objoid, Oid classoid); +extern void removeExtObjInitPriv(Oid objoid, Oid classoid); + + /* ownercheck routines just return true (owner) or false (not) */ extern bool pg_class_ownercheck(Oid class_oid, Oid roleid); extern bool pg_type_ownercheck(Oid type_oid, Oid roleid); diff --git a/src/test/modules/test_pg_dump/expected/test_pg_dump.out b/src/test/modules/test_pg_dump/expected/test_pg_dump.out index 360caa2cb30d57ac3fa151f45897ecfe6d69ab85..c9bc6f762588233ab26c037f1a34051f01ddc647 100644 --- a/src/test/modules/test_pg_dump/expected/test_pg_dump.out +++ b/src/test/modules/test_pg_dump/expected/test_pg_dump.out @@ -1,6 +1,94 @@ -SELECT 1; - ?column? ----------- - 1 -(1 row) - +CREATE ROLE regress_dump_test_role; +CREATE EXTENSION test_pg_dump; +ALTER EXTENSION test_pg_dump ADD DATABASE postgres; -- error +ERROR: syntax error at or near "DATABASE" +LINE 1: ALTER EXTENSION test_pg_dump ADD DATABASE postgres; + ^ +CREATE TABLE test_pg_dump_t1 (c1 int); +CREATE VIEW test_pg_dump_v1 AS SELECT * FROM test_pg_dump_t1; +CREATE MATERIALIZED VIEW test_pg_dump_mv1 AS SELECT * FROM test_pg_dump_t1; +CREATE SCHEMA test_pg_dump_s1; +CREATE TYPE test_pg_dump_e1 AS ENUM ('abc', 'def'); +CREATE AGGREGATE newavg ( + sfunc = int4_avg_accum, basetype = int4, stype = _int8, + finalfunc = int8_avg, + initcond1 = '{0,0}' +); +CREATE FUNCTION test_pg_dump(int) RETURNS int AS $$ +BEGIN +RETURN abs($1); +END +$$ LANGUAGE plpgsql IMMUTABLE; +CREATE OPERATOR ==== ( + LEFTARG = int, + RIGHTARG = int, + PROCEDURE = int4eq, + COMMUTATOR = ==== +); +CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler; +CREATE TYPE casttesttype; +CREATE FUNCTION casttesttype_in(cstring) + RETURNS casttesttype + AS 'textin' + LANGUAGE internal STRICT IMMUTABLE; +NOTICE: return type casttesttype is only a shell +CREATE FUNCTION casttesttype_out(casttesttype) + RETURNS cstring + AS 'textout' + LANGUAGE internal STRICT IMMUTABLE; +NOTICE: argument type casttesttype is only a shell +CREATE TYPE casttesttype ( + internallength = variable, + input = casttesttype_in, + output = casttesttype_out, + alignment = int4 +); +CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; +CREATE FOREIGN DATA WRAPPER dummy; +CREATE SERVER s0 FOREIGN DATA WRAPPER dummy; +CREATE FOREIGN TABLE ft1 ( + c1 integer OPTIONS ("param 1" 'val1') NOT NULL, + c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (c2 <> ''), + c3 date, + CHECK (c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date) +) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); +REVOKE EXECUTE ON FUNCTION test_pg_dump(int) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION test_pg_dump(int) TO regress_dump_test_role; +GRANT SELECT (c1) ON test_pg_dump_t1 TO regress_dump_test_role; +GRANT SELECT ON test_pg_dump_v1 TO regress_dump_test_role; +GRANT USAGE ON FOREIGN DATA WRAPPER dummy TO regress_dump_test_role; +GRANT USAGE ON FOREIGN SERVER s0 TO regress_dump_test_role; +GRANT SELECT (c1) ON ft1 TO regress_dump_test_role; +GRANT SELECT ON ft1 TO regress_dump_test_role; +GRANT UPDATE ON test_pg_dump_mv1 TO regress_dump_test_role; +GRANT USAGE ON SCHEMA test_pg_dump_s1 TO regress_dump_test_role; +GRANT USAGE ON TYPE test_pg_dump_e1 TO regress_dump_test_role; +ALTER EXTENSION test_pg_dump ADD ACCESS METHOD gist2; +ALTER EXTENSION test_pg_dump ADD AGGREGATE newavg(int4); +ALTER EXTENSION test_pg_dump ADD CAST (text AS casttesttype); +ALTER EXTENSION test_pg_dump ADD FOREIGN DATA WRAPPER dummy; +ALTER EXTENSION test_pg_dump ADD FOREIGN TABLE ft1; +ALTER EXTENSION test_pg_dump ADD MATERIALIZED VIEW test_pg_dump_mv1; +ALTER EXTENSION test_pg_dump ADD OPERATOR ==== (int, int); +ALTER EXTENSION test_pg_dump ADD SCHEMA test_pg_dump_s1; +ALTER EXTENSION test_pg_dump ADD SERVER s0; +ALTER EXTENSION test_pg_dump ADD FUNCTION test_pg_dump(int); +ALTER EXTENSION test_pg_dump ADD TABLE test_pg_dump_t1; +ALTER EXTENSION test_pg_dump ADD TYPE test_pg_dump_e1; +ALTER EXTENSION test_pg_dump ADD VIEW test_pg_dump_v1; +REVOKE SELECT (c1) ON test_pg_dump_t1 FROM regress_dump_test_role; +REVOKE SELECT ON test_pg_dump_v1 FROM regress_dump_test_role; +REVOKE USAGE ON FOREIGN DATA WRAPPER dummy FROM regress_dump_test_role; +ALTER EXTENSION test_pg_dump DROP ACCESS METHOD gist2; +ALTER EXTENSION test_pg_dump DROP AGGREGATE newavg(int4); +ALTER EXTENSION test_pg_dump DROP CAST (text AS casttesttype); +ALTER EXTENSION test_pg_dump DROP FOREIGN DATA WRAPPER dummy; +ALTER EXTENSION test_pg_dump DROP FOREIGN TABLE ft1; +ALTER EXTENSION test_pg_dump DROP FUNCTION test_pg_dump(int); +ALTER EXTENSION test_pg_dump DROP MATERIALIZED VIEW test_pg_dump_mv1; +ALTER EXTENSION test_pg_dump DROP OPERATOR ==== (int, int); +ALTER EXTENSION test_pg_dump DROP SCHEMA test_pg_dump_s1; +ALTER EXTENSION test_pg_dump DROP SERVER s0; +ALTER EXTENSION test_pg_dump DROP TABLE test_pg_dump_t1; +ALTER EXTENSION test_pg_dump DROP TYPE test_pg_dump_e1; +ALTER EXTENSION test_pg_dump DROP VIEW test_pg_dump_v1; diff --git a/src/test/modules/test_pg_dump/sql/test_pg_dump.sql b/src/test/modules/test_pg_dump/sql/test_pg_dump.sql index e0ac49d1ecfb8f96f2418b1a87e1d20e587d9d0f..e463dec40402babdbaa7449c3fd0bad6fc6b7088 100644 --- a/src/test/modules/test_pg_dump/sql/test_pg_dump.sql +++ b/src/test/modules/test_pg_dump/sql/test_pg_dump.sql @@ -1 +1,107 @@ -SELECT 1; +CREATE ROLE regress_dump_test_role; +CREATE EXTENSION test_pg_dump; + +ALTER EXTENSION test_pg_dump ADD DATABASE postgres; -- error + +CREATE TABLE test_pg_dump_t1 (c1 int); +CREATE VIEW test_pg_dump_v1 AS SELECT * FROM test_pg_dump_t1; +CREATE MATERIALIZED VIEW test_pg_dump_mv1 AS SELECT * FROM test_pg_dump_t1; +CREATE SCHEMA test_pg_dump_s1; +CREATE TYPE test_pg_dump_e1 AS ENUM ('abc', 'def'); + +CREATE AGGREGATE newavg ( + sfunc = int4_avg_accum, basetype = int4, stype = _int8, + finalfunc = int8_avg, + initcond1 = '{0,0}' +); + +CREATE FUNCTION test_pg_dump(int) RETURNS int AS $$ +BEGIN +RETURN abs($1); +END +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OPERATOR ==== ( + LEFTARG = int, + RIGHTARG = int, + PROCEDURE = int4eq, + COMMUTATOR = ==== +); + +CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler; + +CREATE TYPE casttesttype; + +CREATE FUNCTION casttesttype_in(cstring) + RETURNS casttesttype + AS 'textin' + LANGUAGE internal STRICT IMMUTABLE; +CREATE FUNCTION casttesttype_out(casttesttype) + RETURNS cstring + AS 'textout' + LANGUAGE internal STRICT IMMUTABLE; + +CREATE TYPE casttesttype ( + internallength = variable, + input = casttesttype_in, + output = casttesttype_out, + alignment = int4 +); + +CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; + +CREATE FOREIGN DATA WRAPPER dummy; + +CREATE SERVER s0 FOREIGN DATA WRAPPER dummy; + +CREATE FOREIGN TABLE ft1 ( + c1 integer OPTIONS ("param 1" 'val1') NOT NULL, + c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (c2 <> ''), + c3 date, + CHECK (c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date) +) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); + +REVOKE EXECUTE ON FUNCTION test_pg_dump(int) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION test_pg_dump(int) TO regress_dump_test_role; + +GRANT SELECT (c1) ON test_pg_dump_t1 TO regress_dump_test_role; +GRANT SELECT ON test_pg_dump_v1 TO regress_dump_test_role; +GRANT USAGE ON FOREIGN DATA WRAPPER dummy TO regress_dump_test_role; +GRANT USAGE ON FOREIGN SERVER s0 TO regress_dump_test_role; +GRANT SELECT (c1) ON ft1 TO regress_dump_test_role; +GRANT SELECT ON ft1 TO regress_dump_test_role; +GRANT UPDATE ON test_pg_dump_mv1 TO regress_dump_test_role; +GRANT USAGE ON SCHEMA test_pg_dump_s1 TO regress_dump_test_role; +GRANT USAGE ON TYPE test_pg_dump_e1 TO regress_dump_test_role; + +ALTER EXTENSION test_pg_dump ADD ACCESS METHOD gist2; +ALTER EXTENSION test_pg_dump ADD AGGREGATE newavg(int4); +ALTER EXTENSION test_pg_dump ADD CAST (text AS casttesttype); +ALTER EXTENSION test_pg_dump ADD FOREIGN DATA WRAPPER dummy; +ALTER EXTENSION test_pg_dump ADD FOREIGN TABLE ft1; +ALTER EXTENSION test_pg_dump ADD MATERIALIZED VIEW test_pg_dump_mv1; +ALTER EXTENSION test_pg_dump ADD OPERATOR ==== (int, int); +ALTER EXTENSION test_pg_dump ADD SCHEMA test_pg_dump_s1; +ALTER EXTENSION test_pg_dump ADD SERVER s0; +ALTER EXTENSION test_pg_dump ADD FUNCTION test_pg_dump(int); +ALTER EXTENSION test_pg_dump ADD TABLE test_pg_dump_t1; +ALTER EXTENSION test_pg_dump ADD TYPE test_pg_dump_e1; +ALTER EXTENSION test_pg_dump ADD VIEW test_pg_dump_v1; + +REVOKE SELECT (c1) ON test_pg_dump_t1 FROM regress_dump_test_role; +REVOKE SELECT ON test_pg_dump_v1 FROM regress_dump_test_role; +REVOKE USAGE ON FOREIGN DATA WRAPPER dummy FROM regress_dump_test_role; + +ALTER EXTENSION test_pg_dump DROP ACCESS METHOD gist2; +ALTER EXTENSION test_pg_dump DROP AGGREGATE newavg(int4); +ALTER EXTENSION test_pg_dump DROP CAST (text AS casttesttype); +ALTER EXTENSION test_pg_dump DROP FOREIGN DATA WRAPPER dummy; +ALTER EXTENSION test_pg_dump DROP FOREIGN TABLE ft1; +ALTER EXTENSION test_pg_dump DROP FUNCTION test_pg_dump(int); +ALTER EXTENSION test_pg_dump DROP MATERIALIZED VIEW test_pg_dump_mv1; +ALTER EXTENSION test_pg_dump DROP OPERATOR ==== (int, int); +ALTER EXTENSION test_pg_dump DROP SCHEMA test_pg_dump_s1; +ALTER EXTENSION test_pg_dump DROP SERVER s0; +ALTER EXTENSION test_pg_dump DROP TABLE test_pg_dump_t1; +ALTER EXTENSION test_pg_dump DROP TYPE test_pg_dump_e1; +ALTER EXTENSION test_pg_dump DROP VIEW test_pg_dump_v1; diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 156bd3d1723641ab209b61a2a81515bee4560d06..c8e8d4a94ce3106231912c78539e11f12beb8150 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -236,6 +236,30 @@ my %pgdump_runs = ( # as the regexps are used for each run the test applies to. my %tests = ( + 'ALTER EXTENSION test_pg_dump' => { + create_order => 9, + create_sql => 'ALTER EXTENSION test_pg_dump ADD TABLE regress_pg_dump_table_added;', + regexp => qr/^ + \QCREATE TABLE regress_pg_dump_table_added (\E + \n\s+\Qcol1 integer NOT NULL,\E + \n\s+\Qcol2 integer\E + \n\);\n/xm, + like => { + binary_upgrade => 1, + }, + unlike => { + clean => 1, + clean_if_exists => 1, + createdb => 1, + defaults => 1, + no_privs => 1, + no_owner => 1, + pg_dumpall_globals => 1, + schema_only => 1, + section_pre_data => 1, + section_post_data => 1, + }, }, + 'CREATE EXTENSION test_pg_dump' => { create_order => 2, create_sql => 'CREATE EXTENSION test_pg_dump;', @@ -303,6 +327,30 @@ my %tests = ( section_post_data => 1, }, }, + 'CREATE TABLE regress_pg_dump_table_added' => { + create_order => 7, + create_sql => 'CREATE TABLE regress_pg_dump_table_added (col1 int not null, col2 int);', + regexp => qr/^ + \QCREATE TABLE regress_pg_dump_table_added (\E + \n\s+\Qcol1 integer NOT NULL,\E + \n\s+\Qcol2 integer\E + \n\);\n/xm, + like => { + binary_upgrade => 1, + }, + unlike => { + clean => 1, + clean_if_exists => 1, + createdb => 1, + defaults => 1, + no_privs => 1, + no_owner => 1, + pg_dumpall_globals => 1, + schema_only => 1, + section_pre_data => 1, + section_post_data => 1, + }, }, + 'CREATE SEQUENCE regress_pg_dump_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_seq\E @@ -413,6 +461,50 @@ my %tests = ( section_post_data => 1, }, }, + 'GRANT SELECT regress_pg_dump_table_added pre-ALTER EXTENSION' => { + create_order => 8, + create_sql => 'GRANT SELECT ON regress_pg_dump_table_added TO regress_dump_test_role;', + regexp => qr/^ + \QGRANT SELECT ON TABLE regress_pg_dump_table_added TO regress_dump_test_role;\E + \n/xm, + like => { + binary_upgrade => 1, + }, + unlike => { + clean => 1, + clean_if_exists => 1, + createdb => 1, + defaults => 1, + no_privs => 1, + no_owner => 1, + pg_dumpall_globals => 1, + schema_only => 1, + section_pre_data => 1, + section_post_data => 1, + }, }, + + 'REVOKE SELECT regress_pg_dump_table_added post-ALTER EXTENSION' => { + create_order => 10, + create_sql => 'REVOKE SELECT ON regress_pg_dump_table_added FROM regress_dump_test_role;', + regexp => qr/^ + \QREVOKE SELECT ON TABLE regress_pg_dump_table_added FROM regress_dump_test_role;\E + \n/xm, + like => { + binary_upgrade => 1, + clean => 1, + clean_if_exists => 1, + createdb => 1, + defaults => 1, + no_owner => 1, + schema_only => 1, + section_pre_data => 1, + }, + unlike => { + no_privs => 1, + pg_dumpall_globals => 1, + section_post_data => 1, + }, }, + 'GRANT SELECT ON TABLE regress_pg_dump_table' => { regexp => qr/^ \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\E\n