]> granicus.if.org Git - postgresql/commitdiff
Handle ALTER EXTENSION ADD/DROP with pg_init_privs
authorStephen Frost <sfrost@snowman.net>
Mon, 30 Jan 2017 04:05:07 +0000 (23:05 -0500)
committerStephen Frost <sfrost@snowman.net>
Mon, 30 Jan 2017 04:05:07 +0000 (23:05 -0500)
In commit 6c268df, pg_init_privs was added to track the initial
privileges of catalog objects and extensions.  Unfortunately, that
commit didn't include understanding of ALTER EXTENSION ADD/DROP, which
allows the objects associated with an extension to be changed after the
initial CREATE EXTENSION script has been run.

The result of this meant that ACLs for objects added through
ALTER EXTENSION ADD were not recorded into pg_init_privs and we would
end up including those ACLs in pg_dump when we shouldn't have.

This commit corrects that by making sure to have pg_init_privs updated
when ALTER EXTENSION ADD/DROP is run, recording the permissions as they
are at ALTER EXTENSION ADD time, and removing any if/when ALTER
EXTENSION DROP is called.

This issue was pointed out by Moshe Jacobson as commentary on bug #14456
(which was actually a bug about versions prior to 9.6 not handling
custom ACLs on extensions correctly, an issue now addressed with
pg_init_privs in 9.6).

Back-patch to 9.6 where pg_init_privs was introduced.

src/backend/catalog/aclchk.c
src/backend/commands/extension.c
src/include/utils/acl.h
src/test/modules/test_pg_dump/expected/test_pg_dump.out
src/test/modules/test_pg_dump/sql/test_pg_dump.sql
src/test/modules/test_pg_dump/t/001_base.pl

index f4df6df10fa71c50194f0d0c749b95c11ba375f6..00a9aea556543574952ee2018a47c3a78defbb33 100644 (file)
 #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);
index 9680d986a014e4ee39c67ddaeec775616743d5a8..f23c6977cb27ae0d98fe372d848db806ce76d094 100644 (file)
@@ -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);
index 686141b5f9abb0d842da71edb14ac7b6ec67ea59..0d118525c9cb3dd1df43df82ff4c3ed24c10c4c1 100644 (file)
@@ -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);
index 360caa2cb30d57ac3fa151f45897ecfe6d69ab85..c9bc6f762588233ab26c037f1a34051f01ddc647 100644 (file)
@@ -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;
index e0ac49d1ecfb8f96f2418b1a87e1d20e587d9d0f..e463dec40402babdbaa7449c3fd0bad6fc6b7088 100644 (file)
@@ -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;
index 156bd3d1723641ab209b61a2a81515bee4560d06..c8e8d4a94ce3106231912c78539e11f12beb8150 100644 (file)
@@ -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