Re: [HACKERS] Inconsistent syntax in GRANT - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [HACKERS] Inconsistent syntax in GRANT
Date
Msg-id 200601210218.k0L2IEk14807@candle.pha.pa.us
Whole thread Raw
In response to Re: [HACKERS] Inconsistent syntax in GRANT  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-patches
Updated patch applied.  I decided Tom was right to just ignore invalid
sequence permission from pre-8.2 databases, rather than try to use GRANT
TABLE;  there was no reason to do it and avoiding it made the code
cleaner and more robust.

The changes were:

Add GRANT ON SEQUENCE syntax to support sequence-only permissions.
Continue to support GRANT ON [TABLE] for sequences for backward
compatibility;  issue warning for invalid sequence permissions.

[ Backward compatibility warning message.]

Add USAGE permission for sequences that allows only currval() and
nextval(), not setval().

Mention object name in grant/revoke warnings because of possible
multi-object operations.

---------------------------------------------------------------------------

Bruce Momjian wrote:
> Tom Lane wrote:
> > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > > Tom Lane wrote:
> > >> Just ignore the inapplicable permissions during pg_dump.  I think you're
> > >> making this harder than it needs to be...
> >
> > >     check all permission bits
> > >     call object-type-specific routine
> > >         loop over each object and set permission bits
> >
> > > so, to fix this, I would need to move the permission bit checks into
> > > object-type-specific routines so that I could check the permission bits
> > > for each object, rather than once in a single place.
> >
> > You'd have to allow the union of relation and sequence rights during the
> > conversion to bitmask form in ExecuteGrantStmt, and then check more
> > closely inside the per-object loop in ExecGrant_Relation, but that
> > doesn't seem like a showstopper to me.  It certainly seems more pleasant
> > than exposing bizarre restrictions to users because we're sharing code
> > between the cases.
>
> Your idea of using a union of permission bits was very helpful.  I was
> afraid I was going to have to loop over every permission bit again in
> the table/sequence grant permission code, but the union allowed for a
> very simple check in that code.
>
> It allows for better code checks and I think it behaves as expected:
>
>     test=> CREATE TABLE tab(x INTEGER);
>     CREATE TABLE
>     test=> CREATE SEQUENCE seq;
>     CREATE SEQUENCE
>     test=> GRANT ALL ON seq, tab TO PUBLIC;
>     GRANT
>     test=> REVOKE USAGE ON seq, tab FROM PUBLIC;
>     ERROR:  invalid privilege type USAGE for table
>     test=> REVOKE SELECT ON seq, tab FROM PUBLIC;
>     REVOKE
>     test=> REVOKE DELETE ON seq, tab FROM PUBLIC;
>     WARNING:  sequence "seq" only supports USAGE, SELECT, AND UPDATE
>     WARNING:  no privileges could be revoked for "seq"
>     REVOKE
>
> and pg_dump has:
>
>     GRANT USAGE,UPDATE ON SEQUENCE x TO PUBLIC;
>
>     GRANT INSERT,RULE,UPDATE,REFERENCES,TRIGGER ON TABLE xx TO PUBLIC;
>
> Note I had to add the object name to the warning message so it is clear
> which object permission changes did succeed.  I have updated the patch.
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/ref/grant.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/grant.sgml,v
retrieving revision 1.50
diff -c -c -r1.50 grant.sgml
*** doc/src/sgml/ref/grant.sgml    20 Oct 2005 19:18:01 -0000    1.50
--- doc/src/sgml/ref/grant.sgml    21 Jan 2006 01:16:10 -0000
***************
*** 25,30 ****
--- 25,35 ----
      ON [ TABLE ] <replaceable class="PARAMETER">tablename</replaceable> [, ...]
      TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable
class="PARAMETER">groupname</replaceable>| PUBLIC } [, ...] [ WITH GRANT OPTION ] 

+ GRANT { { USAGE | SELECT | UPDATE }
+     [,...] | ALL [ PRIVILEGES ] }
+     ON SEQUENCE <replaceable class="PARAMETER">sequencename</replaceable> [, ...]
+     TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable
class="PARAMETER">groupname</replaceable>| PUBLIC } [, ...] [ WITH GRANT OPTION ] 
+
  GRANT { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] }
      ON DATABASE <replaceable>dbname</replaceable> [, ...]
      TO { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable
class="PARAMETER">groupname</replaceable>| PUBLIC } [, ...] [ WITH GRANT OPTION ] 
***************
*** 260,265 ****
--- 265,274 ----
         also met).  Essentially this allows the grantee to <quote>look up</>
         objects within the schema.
        </para>
+       <para>
+        For sequences, this privilege allows the use of the
+        <function>currval</function> and <function>nextval</function> functions.
+       </para>
       </listitem>
      </varlistentry>

***************
*** 511,517 ****

     <para>
      The <literal>RULE</literal> privilege, and privileges on
!     databases, tablespaces, schemas, languages, and sequences are
      <productname>PostgreSQL</productname> extensions.
     </para>
   </refsect1>
--- 520,526 ----

     <para>
      The <literal>RULE</literal> privilege, and privileges on
!     databases, tablespaces, schemas, and languages are
      <productname>PostgreSQL</productname> extensions.
     </para>
   </refsect1>
Index: doc/src/sgml/ref/revoke.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/revoke.sgml,v
retrieving revision 1.35
diff -c -c -r1.35 revoke.sgml
*** doc/src/sgml/ref/revoke.sgml    20 Oct 2005 19:18:01 -0000    1.35
--- doc/src/sgml/ref/revoke.sgml    21 Jan 2006 01:16:10 -0000
***************
*** 28,33 ****
--- 28,40 ----
      [ CASCADE | RESTRICT ]

  REVOKE [ GRANT OPTION FOR ]
+     { { USAGE | SELECT | UPDATE }
+     [,...] | ALL [ PRIVILEGES ] }
+     ON SEQUENCE <replaceable class="PARAMETER">sequencename</replaceable> [, ...]
+     FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable
class="PARAMETER">groupname</replaceable>| PUBLIC } [, ...] 
+     [ CASCADE | RESTRICT ]
+
+ REVOKE [ GRANT OPTION FOR ]
      { { CREATE | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] }
      ON DATABASE <replaceable>dbname</replaceable> [, ...]
      FROM { <replaceable class="PARAMETER">username</replaceable> | GROUP <replaceable
class="PARAMETER">groupname</replaceable>| PUBLIC } [, ...] 
Index: src/backend/catalog/aclchk.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v
retrieving revision 1.123
diff -c -c -r1.123 aclchk.c
*** src/backend/catalog/aclchk.c    1 Dec 2005 02:03:00 -0000    1.123
--- src/backend/catalog/aclchk.c    21 Jan 2006 01:16:11 -0000
***************
*** 164,169 ****
--- 164,172 ----
          case ACL_KIND_CLASS:
              whole_mask = ACL_ALL_RIGHTS_RELATION;
              break;
+         case ACL_KIND_SEQUENCE:
+             whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
+             break;
          case ACL_KIND_DATABASE:
              whole_mask = ACL_ALL_RIGHTS_DATABASE;
              break;
***************
*** 212,233 ****
          if (this_privileges == 0)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
!                      errmsg("no privileges were granted")));
          else if (!all_privs && this_privileges != privileges)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
!                      errmsg("not all privileges were granted")));
      }
      else
      {
          if (this_privileges == 0)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
!                      errmsg("no privileges could be revoked")));
          else if (!all_privs && this_privileges != privileges)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
!                      errmsg("not all privileges could be revoked")));
      }

      return this_privileges;
--- 215,236 ----
          if (this_privileges == 0)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
!                      errmsg("no privileges were granted for \"%s\"", objname)));
          else if (!all_privs && this_privileges != privileges)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
!                      errmsg("not all privileges were granted for \"%s\"", objname)));
      }
      else
      {
          if (this_privileges == 0)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
!                      errmsg("no privileges could be revoked for \"%s\"", objname)));
          else if (!all_privs && this_privileges != privileges)
              ereport(WARNING,
                      (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
!                      errmsg("not all privileges could be revoked for \"%s\"", objname)));
      }

      return this_privileges;
***************
*** 282,290 ****
       */
      switch (stmt->objtype)
      {
          case ACL_OBJECT_RELATION:
!             all_privileges = ACL_ALL_RIGHTS_RELATION;
!             errormsg = _("invalid privilege type %s for table");
              break;
          case ACL_OBJECT_DATABASE:
              all_privileges = ACL_ALL_RIGHTS_DATABASE;
--- 285,302 ----
       */
      switch (stmt->objtype)
      {
+         /*
+          *    Because this might be a sequence, we test both relation
+          *    and sequence bits, and later do a more limited test
+          *    when we know the object type.
+          */
          case ACL_OBJECT_RELATION:
!             all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
!             errormsg = _("invalid privilege type %s for relation");
!             break;
!         case ACL_OBJECT_SEQUENCE:
!             all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
!             errormsg = _("invalid privilege type %s for sequence");
              break;
          case ACL_OBJECT_DATABASE:
              all_privileges = ACL_ALL_RIGHTS_DATABASE;
***************
*** 327,332 ****
--- 339,345 ----
      {
          istmt.all_privs = false;
          istmt.privileges = ACL_NO_RIGHTS;
+
          foreach(cell, stmt->privileges)
          {
              char       *privname = strVal(lfirst(cell));
***************
*** 356,361 ****
--- 369,375 ----
      switch (istmt->objtype)
      {
          case ACL_OBJECT_RELATION:
+         case ACL_OBJECT_SEQUENCE:
              ExecGrant_Relation(istmt);
              break;
          case ACL_OBJECT_DATABASE:
***************
*** 395,400 ****
--- 409,415 ----
      switch (objtype)
      {
          case ACL_OBJECT_RELATION:
+         case ACL_OBJECT_SEQUENCE:
              foreach(cell, objnames)
              {
                  Oid            relOid;
***************
*** 523,537 ****
      return objects;
  }

  static void
  ExecGrant_Relation(InternalGrant *istmt)
  {
      Relation    relation;
      ListCell   *cell;

-     if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
-         istmt->privileges = ACL_ALL_RIGHTS_RELATION;
-
      relation = heap_open(RelationRelationId, RowExclusiveLock);

      foreach(cell, istmt->objects)
--- 538,552 ----
      return objects;
  }

+ /*
+  *    This processes both sequences and non-sequences.
+  */
  static void
  ExecGrant_Relation(InternalGrant *istmt)
  {
      Relation    relation;
      ListCell   *cell;

      relation = heap_open(RelationRelationId, RowExclusiveLock);

      foreach(cell, istmt->objects)
***************
*** 577,582 ****
--- 592,660 ----
                       errmsg("\"%s\" is a composite type",
                              NameStr(pg_class_tuple->relname))));

+         /* Used GRANT SEQUENCE on a non-sequence? */
+         if (istmt->objtype == ACL_OBJECT_SEQUENCE &&
+             pg_class_tuple->relkind != RELKIND_SEQUENCE)
+             ereport(ERROR,
+                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                      errmsg("\"%s\" is not a sequence",
+                             NameStr(pg_class_tuple->relname))));
+
+         /* Adjust the default permissions based on whether it is a sequence */
+         if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+         {
+             if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
+                 this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+             else
+                 this_privileges = ACL_ALL_RIGHTS_RELATION;
+         }
+         else
+             this_privileges = istmt->privileges;
+
+         /*
+          *    The GRANT TABLE syntax can be used for sequences and
+          *    non-sequences, so we have to look at the relkind to
+          *    determine the supported permissions.  The OR of
+          *    table and sequence permissions were already checked.
+          */
+         if (istmt->objtype == ACL_OBJECT_RELATION)
+         {
+             if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
+             {
+                 /*
+                  *    For backward compatibility, throw just a warning
+                  *    for invalid sequence permissions when using the
+                  *    non-sequence GRANT syntax is used.
+                  */
+                 if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
+                 {
+                     /*
+                      *    Mention the object name because the user needs to
+                      *    know which operations succeeded.  This is required
+                      *    because WARNING allows the command to continue.
+                      */
+                     ereport(WARNING,
+                             (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+                              errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE",
+                                     NameStr(pg_class_tuple->relname))));
+                     this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
+                 }
+             }
+             else
+             {
+                 if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
+                     /*
+                      *    USAGE is the only permission supported by sequences
+                      *    but not by non-sequences.  Don't mention the object
+                      *    name because we didn't in the combined TABLE |
+                      *    SEQUENCE check.
+                      */
+                     ereport(ERROR,
+                             (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+                              errmsg("invalid privilege type USAGE for table")));
+             }
+         }
+
          /*
           * Get owner ID and working copy of existing ACL. If there's no ACL,
           * substitute the proper default.
***************
*** 585,596 ****
          aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
                                     &isNull);
          if (isNull)
!             old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
          else
              old_acl = DatumGetAclPCopy(aclDatum);

          /* Determine ID to do the grant as, and available grant options */
!         select_best_grantor(GetUserId(), istmt->privileges,
                              old_acl, ownerId,
                              &grantorId, &avail_goptions);

--- 663,676 ----
          aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
                                     &isNull);
          if (isNull)
!             old_acl = acldefault(pg_class_tuple->relkind == RELKIND_SEQUENCE ?
!                                  ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
!                                  ownerId);
          else
              old_acl = DatumGetAclPCopy(aclDatum);

          /* Determine ID to do the grant as, and available grant options */
!         select_best_grantor(GetUserId(), this_privileges,
                              old_acl, ownerId,
                              &grantorId, &avail_goptions);

***************
*** 600,607 ****
           */
          this_privileges =
              restrict_and_check_grant(istmt->is_grant, avail_goptions,
!                                      istmt->all_privs, istmt->privileges,
!                                      relOid, grantorId, ACL_KIND_CLASS,
                                       NameStr(pg_class_tuple->relname));

          /*
--- 680,689 ----
           */
          this_privileges =
              restrict_and_check_grant(istmt->is_grant, avail_goptions,
!                                      istmt->all_privs, this_privileges,
!                                      relOid, grantorId,
!                                      pg_class_tuple->relkind == RELKIND_SEQUENCE
!                                         ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
                                       NameStr(pg_class_tuple->relname));

          /*
***************
*** 1336,1341 ****
--- 1418,1425 ----
  {
      /* ACL_KIND_CLASS */
      gettext_noop("permission denied for relation %s"),
+     /* ACL_KIND_SEQUENCE */
+     gettext_noop("permission denied for sequence %s"),
      /* ACL_KIND_DATABASE */
      gettext_noop("permission denied for database %s"),
      /* ACL_KIND_PROC */
***************
*** 1360,1365 ****
--- 1444,1451 ----
  {
      /* ACL_KIND_CLASS */
      gettext_noop("must be owner of relation %s"),
+     /* ACL_KIND_SEQUENCE */
+     gettext_noop("must be owner of sequence %s"),
      /* ACL_KIND_DATABASE */
      gettext_noop("must be owner of database %s"),
      /* ACL_KIND_PROC */
***************
*** 1439,1444 ****
--- 1525,1531 ----
      switch (objkind)
      {
          case ACL_KIND_CLASS:
+         case ACL_KIND_SEQUENCE:
              return pg_class_aclmask(table_oid, roleid, mask, how);
          case ACL_KIND_DATABASE:
              return pg_database_aclmask(table_oid, roleid, mask, how);
***************
*** 1500,1508 ****
       *
       * As of 7.4 we have some updatable system views; those shouldn't be
       * protected in this way.  Assume the view rules can take care of
!      * themselves.
       */
!     if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
          IsSystemClass(classForm) &&
          classForm->relkind != RELKIND_VIEW &&
          !has_rolcatupdate(roleid) &&
--- 1587,1595 ----
       *
       * As of 7.4 we have some updatable system views; those shouldn't be
       * protected in this way.  Assume the view rules can take care of
!      * themselves.  ACL_USAGE is if we ever have system sequences.
       */
!     if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_USAGE)) &&
          IsSystemClass(classForm) &&
          classForm->relkind != RELKIND_VIEW &&
          !has_rolcatupdate(roleid) &&
***************
*** 1511,1517 ****
  #ifdef ACLDEBUG
          elog(DEBUG2, "permission denied for system catalog update");
  #endif
!         mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE);
      }

      /*
--- 1598,1604 ----
  #ifdef ACLDEBUG
          elog(DEBUG2, "permission denied for system catalog update");
  #endif
!         mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_USAGE);
      }

      /*
***************
*** 1536,1542 ****
      if (isNull)
      {
          /* No ACL, so build default ACL */
!         acl = acldefault(ACL_OBJECT_RELATION, ownerId);
          aclDatum = (Datum) 0;
      }
      else
--- 1623,1631 ----
      if (isNull)
      {
          /* No ACL, so build default ACL */
!         acl = acldefault(classForm->relkind == RELKIND_SEQUENCE ?
!                             ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
!                          ownerId);
          aclDatum = (Datum) 0;
      }
      else
Index: src/backend/catalog/pg_shdepend.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/catalog/pg_shdepend.c,v
retrieving revision 1.6
diff -c -c -r1.6 pg_shdepend.c
*** src/backend/catalog/pg_shdepend.c    1 Dec 2005 02:03:00 -0000    1.6
--- src/backend/catalog/pg_shdepend.c    21 Jan 2006 01:16:12 -0000
***************
*** 1133,1140 ****
                      switch (sdepForm->classid)
                      {
                          case RelationRelationId:
!                             istmt.objtype = ACL_OBJECT_RELATION;
                              break;
                          case DatabaseRelationId:
                              istmt.objtype = ACL_OBJECT_DATABASE;
                              break;
--- 1133,1157 ----
                      switch (sdepForm->classid)
                      {
                          case RelationRelationId:
!                         {
!                             /* is it a sequence or non-sequence? */
!                             Form_pg_class pg_class_tuple;
!                             HeapTuple    tuple;
!
!                             tuple = SearchSysCache(RELOID,
!                                 ObjectIdGetDatum(sdepForm->objid),
!                                 0, 0, 0);
!                             if (!HeapTupleIsValid(tuple))
!                                 elog(ERROR, "cache lookup failed for relation %u",
!                                             sdepForm->objid);
!                             pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
!                             if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
!                                 istmt.objtype = ACL_OBJECT_SEQUENCE;
!                             else
!                                 istmt.objtype = ACL_OBJECT_RELATION;
!                             ReleaseSysCache(tuple);
                              break;
+                         }
                          case DatabaseRelationId:
                              istmt.objtype = ACL_OBJECT_DATABASE;
                              break;
Index: src/backend/commands/sequence.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/sequence.c,v
retrieving revision 1.126
diff -c -c -r1.126 sequence.c
*** src/backend/commands/sequence.c    22 Nov 2005 18:17:09 -0000    1.126
--- src/backend/commands/sequence.c    21 Jan 2006 01:16:12 -0000
***************
*** 422,428 ****
      /* open and AccessShareLock sequence */
      init_sequence(relid, &elm, &seqrel);

!     if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("permission denied for sequence %s",
--- 422,429 ----
      /* open and AccessShareLock sequence */
      init_sequence(relid, &elm, &seqrel);

!     if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_USAGE) != ACLCHECK_OK &&
!         pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("permission denied for sequence %s",
***************
*** 613,619 ****
      /* open and AccessShareLock sequence */
      init_sequence(relid, &elm, &seqrel);

!     if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("permission denied for sequence %s",
--- 614,621 ----
      /* open and AccessShareLock sequence */
      init_sequence(relid, &elm, &seqrel);

!     if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK &&
!         pg_class_aclcheck(elm->relid, GetUserId(), ACL_USAGE) != ACLCHECK_OK)
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("permission denied for sequence %s",
***************
*** 657,663 ****
      /* nextval() must have already been called for this sequence */
      Assert(last_used_seq->increment != 0);

!     if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("permission denied for sequence %s",
--- 659,666 ----
      /* nextval() must have already been called for this sequence */
      Assert(last_used_seq->increment != 0);

!     if (pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK &&
!         pg_class_aclcheck(last_used_seq->relid, GetUserId(), ACL_USAGE) != ACLCHECK_OK)
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("permission denied for sequence %s",
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.521
diff -c -c -r2.521 gram.y
*** src/backend/parser/gram.y    29 Dec 2005 04:53:18 -0000    2.521
--- src/backend/parser/gram.y    21 Jan 2006 01:16:16 -0000
***************
*** 3322,3327 ****
--- 3322,3334 ----
                      n->objs = $2;
                      $$ = n;
                  }
+             | SEQUENCE qualified_name_list
+                 {
+                     PrivTarget *n = makeNode(PrivTarget);
+                     n->objtype = ACL_OBJECT_SEQUENCE;
+                     n->objs = $2;
+                     $$ = n;
+                 }
              | FUNCTION function_with_argtypes_list
                  {
                      PrivTarget *n = makeNode(PrivTarget);
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v
retrieving revision 1.129
diff -c -c -r1.129 acl.c
*** src/backend/utils/adt/acl.c    18 Nov 2005 02:38:23 -0000    1.129
--- src/backend/utils/adt/acl.c    21 Jan 2006 01:16:17 -0000
***************
*** 545,550 ****
--- 545,554 ----
              world_default = ACL_NO_RIGHTS;
              owner_default = ACL_ALL_RIGHTS_RELATION;
              break;
+         case ACL_OBJECT_SEQUENCE:
+             world_default = ACL_NO_RIGHTS;
+             owner_default = ACL_ALL_RIGHTS_SEQUENCE;
+             break;
          case ACL_OBJECT_DATABASE:
              world_default = ACL_CREATE_TEMP;    /* not NO_RIGHTS! */
              owner_default = ACL_ALL_RIGHTS_DATABASE;
Index: src/bin/pg_dump/dumputils.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v
retrieving revision 1.24
diff -c -c -r1.24 dumputils.c
*** src/bin/pg_dump/dumputils.c    11 Jan 2006 21:24:30 -0000    1.24
--- src/bin/pg_dump/dumputils.c    21 Jan 2006 01:16:18 -0000
***************
*** 22,29 ****
  #define supports_grant_options(version) ((version) >= 70400)

  static bool parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion,
!              PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo);
  static char *copyAclUserName(PQExpBuffer output, char *input);
  static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
--- 22,28 ----
  #define supports_grant_options(version) ((version) >= 70400)

  static bool parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo);
  static char *copyAclUserName(PQExpBuffer output, char *input);
  static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
***************
*** 326,332 ****
   *
   *    name: the object name, in the form to use in the commands (already quoted)
   *    type: the object type (as seen in GRANT command: must be one of
!  *        TABLE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE)
   *    acls: the ACL string fetched from the database
   *    owner: username of object owner (will be passed through fmtId); can be
   *        NULL or empty string to indicate "no owner known"
--- 325,331 ----
   *
   *    name: the object name, in the form to use in the commands (already quoted)
   *    type: the object type (as seen in GRANT command: must be one of
!  *        TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE)
   *    acls: the ACL string fetched from the database
   *    owner: username of object owner (will be passed through fmtId); can be
   *        NULL or empty string to indicate "no owner known"
***************
*** 515,522 ****
   */
  static bool
  parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion,
!              PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo)
  {
      char       *buf;
--- 514,520 ----
   */
  static bool
  parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion, PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo)
  {
      char       *buf;
***************
*** 547,552 ****
--- 545,551 ----

      /* privilege codes */
  #define CONVERT_PRIV(code, keywd) \
+ do { \
      if ((pos = strchr(eqpos + 1, code))) \
      { \
          if (*(pos + 1) == '*') \
***************
*** 561,589 ****
          } \
      } \
      else \
!         all_with_go = all_without_go = false

      resetPQExpBuffer(privs);
      resetPQExpBuffer(privswgo);

!     if (strcmp(type, "TABLE") == 0)
      {
-         CONVERT_PRIV('a', "INSERT");
          CONVERT_PRIV('r', "SELECT");
!         CONVERT_PRIV('R', "RULE");
!
!         if (remoteVersion >= 70200)
          {
!             CONVERT_PRIV('w', "UPDATE");
!             CONVERT_PRIV('d', "DELETE");
!             CONVERT_PRIV('x', "REFERENCES");
!             CONVERT_PRIV('t', "TRIGGER");
          }
          else
-         {
              /* 7.0 and 7.1 have a simpler worldview */
              CONVERT_PRIV('w', "UPDATE,DELETE");
-         }
      }
      else if (strcmp(type, "FUNCTION") == 0)
          CONVERT_PRIV('X', "EXECUTE");
--- 560,597 ----
          } \
      } \
      else \
!         all_with_go = all_without_go = false; \
! } while (0)

      resetPQExpBuffer(privs);
      resetPQExpBuffer(privswgo);

!     if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0)
      {
          CONVERT_PRIV('r', "SELECT");
!
!         if (strcmp(type, "SEQUENCE") == 0)
!             /* sequence only */
!             CONVERT_PRIV('U', "USAGE");
!         else
          {
!             /* table only */
!             CONVERT_PRIV('a', "INSERT");
!             CONVERT_PRIV('R', "RULE");
!             if (remoteVersion >= 70200)
!             {
!                 CONVERT_PRIV('d', "DELETE");
!                 CONVERT_PRIV('x', "REFERENCES");
!                 CONVERT_PRIV('t', "TRIGGER");
!             }
          }
+
+         /* UPDATE */
+         if (remoteVersion >= 70200 || strcmp(type, "SEQUENCE") == 0)
+             CONVERT_PRIV('w', "UPDATE");
          else
              /* 7.0 and 7.1 have a simpler worldview */
              CONVERT_PRIV('w', "UPDATE,DELETE");
      }
      else if (strcmp(type, "FUNCTION") == 0)
          CONVERT_PRIV('X', "EXECUTE");
Index: src/bin/pg_dump/pg_backup_archiver.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.c,v
retrieving revision 1.118
diff -c -c -r1.118 pg_backup_archiver.c
*** src/bin/pg_dump/pg_backup_archiver.c    22 Nov 2005 18:17:28 -0000    1.118
--- src/bin/pg_dump/pg_backup_archiver.c    21 Jan 2006 01:16:19 -0000
***************
*** 1889,1895 ****
              if (strcmp(ropt->schemaNames, te->namespace) != 0)
                  return 0;
          }
!         if ((strcmp(te->desc, "TABLE") == 0) || (strcmp(te->desc, "TABLE DATA") == 0))
          {
              if (!ropt->selTable)
                  return 0;
--- 1889,1896 ----
              if (strcmp(ropt->schemaNames, te->namespace) != 0)
                  return 0;
          }
!         if (strcmp(te->desc, "TABLE") == 0 ||
!             strcmp(te->desc, "TABLE DATA") == 0)
          {
              if (!ropt->selTable)
                  return 0;
***************
*** 2276,2283 ****
      const char *type = te->desc;

      /* Use ALTER TABLE for views and sequences */
!     if (strcmp(type, "VIEW") == 0 ||
!         strcmp(type, "SEQUENCE") == 0)
          type = "TABLE";

      /* objects named by a schema and name */
--- 2277,2283 ----
      const char *type = te->desc;

      /* Use ALTER TABLE for views and sequences */
!     if (strcmp(type, "VIEW") == 0 || strcmp(type, "SEQUENCE") == 0)
          type = "TABLE";

      /* objects named by a schema and name */
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.426
diff -c -c -r1.426 pg_dump.c
*** src/bin/pg_dump/pg_dump.c    9 Jan 2006 21:16:17 -0000    1.426
--- src/bin/pg_dump/pg_dump.c    21 Jan 2006 01:16:21 -0000
***************
*** 6788,6794 ****

          /* Handle the ACL here */
          namecopy = strdup(fmtId(tbinfo->dobj.name));
!         dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, "TABLE",
                  namecopy, tbinfo->dobj.name,
                  tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
                  tbinfo->relacl);
--- 6788,6795 ----

          /* Handle the ACL here */
          namecopy = strdup(fmtId(tbinfo->dobj.name));
!         dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
!                 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE",
                  namecopy, tbinfo->dobj.name,
                  tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
                  tbinfo->relacl);
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.298
diff -c -c -r1.298 parsenodes.h
*** src/include/nodes/parsenodes.h    7 Dec 2005 15:20:55 -0000    1.298
--- src/include/nodes/parsenodes.h    21 Jan 2006 01:16:22 -0000
***************
*** 884,890 ****
   */
  typedef enum GrantObjectType
  {
!     ACL_OBJECT_RELATION,        /* table, view, sequence */
      ACL_OBJECT_DATABASE,        /* database */
      ACL_OBJECT_FUNCTION,        /* function */
      ACL_OBJECT_LANGUAGE,        /* procedural language */
--- 884,891 ----
   */
  typedef enum GrantObjectType
  {
!     ACL_OBJECT_RELATION,        /* table, view */
!     ACL_OBJECT_SEQUENCE,        /* sequence */
      ACL_OBJECT_DATABASE,        /* database */
      ACL_OBJECT_FUNCTION,        /* function */
      ACL_OBJECT_LANGUAGE,        /* procedural language */
Index: src/include/utils/acl.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/acl.h,v
retrieving revision 1.91
diff -c -c -r1.91 acl.h
*** src/include/utils/acl.h    1 Dec 2005 02:03:01 -0000    1.91
--- src/include/utils/acl.h    21 Jan 2006 01:16:23 -0000
***************
*** 143,148 ****
--- 143,149 ----
   * Bitmasks defining "all rights" for each supported object type
   */
  #define ACL_ALL_RIGHTS_RELATION
(ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_RULE|ACL_REFERENCES|ACL_TRIGGER)
+ #define ACL_ALL_RIGHTS_SEQUENCE        (ACL_USAGE|ACL_SELECT|ACL_UPDATE)
  #define ACL_ALL_RIGHTS_DATABASE        (ACL_CREATE|ACL_CREATE_TEMP)
  #define ACL_ALL_RIGHTS_FUNCTION        (ACL_EXECUTE)
  #define ACL_ALL_RIGHTS_LANGUAGE        (ACL_USAGE)
***************
*** 169,174 ****
--- 170,176 ----
  typedef enum AclObjectKind
  {
      ACL_KIND_CLASS,                /* pg_class */
+     ACL_KIND_SEQUENCE,            /* pg_sequence */
      ACL_KIND_DATABASE,            /* pg_database */
      ACL_KIND_PROC,                /* pg_proc */
      ACL_KIND_OPER,                /* pg_operator */
Index: src/test/regress/expected/privileges.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/privileges.out,v
retrieving revision 1.32
diff -c -c -r1.32 privileges.out
*** src/test/regress/expected/privileges.out    15 Aug 2005 02:40:30 -0000    1.32
--- src/test/regress/expected/privileges.out    21 Jan 2006 01:16:23 -0000
***************
*** 90,96 ****
  COPY atest2 FROM stdin; -- fail
  ERROR:  permission denied for relation atest2
  GRANT ALL ON atest1 TO PUBLIC; -- fail
! WARNING:  no privileges were granted
  -- checks in subquery, both ok
  SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) );
   a | b
--- 90,96 ----
  COPY atest2 FROM stdin; -- fail
  ERROR:  permission denied for relation atest2
  GRANT ALL ON atest1 TO PUBLIC; -- fail
! WARNING:  no privileges were granted for "atest1"
  -- checks in subquery, both ok
  SELECT * FROM atest1 WHERE ( b IN ( SELECT col1 FROM atest2 ) );
   a | b
***************
*** 227,233 ****
  HINT:  Only superusers may use untrusted languages.
  SET SESSION AUTHORIZATION regressuser1;
  GRANT USAGE ON LANGUAGE sql TO regressuser2; -- fail
! WARNING:  no privileges were granted
  CREATE FUNCTION testfunc1(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql;
  CREATE FUNCTION testfunc2(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql;
  REVOKE ALL ON FUNCTION testfunc1(int), testfunc2(int) FROM PUBLIC;
--- 227,233 ----
  HINT:  Only superusers may use untrusted languages.
  SET SESSION AUTHORIZATION regressuser1;
  GRANT USAGE ON LANGUAGE sql TO regressuser2; -- fail
! WARNING:  no privileges were granted for "sql"
  CREATE FUNCTION testfunc1(int) RETURNS int AS 'select 2 * $1;' LANGUAGE sql;
  CREATE FUNCTION testfunc2(int) RETURNS int AS 'select 3 * $1;' LANGUAGE sql;
  REVOKE ALL ON FUNCTION testfunc1(int), testfunc2(int) FROM PUBLIC;
***************
*** 551,557 ****
  SET SESSION AUTHORIZATION regressuser2;
  GRANT SELECT ON atest4 TO regressuser3;
  GRANT UPDATE ON atest4 TO regressuser3; -- fail
! WARNING:  no privileges were granted
  SET SESSION AUTHORIZATION regressuser1;
  REVOKE SELECT ON atest4 FROM regressuser3; -- does nothing
  SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- true
--- 551,557 ----
  SET SESSION AUTHORIZATION regressuser2;
  GRANT SELECT ON atest4 TO regressuser3;
  GRANT UPDATE ON atest4 TO regressuser3; -- fail
! WARNING:  no privileges were granted for "atest4"
  SET SESSION AUTHORIZATION regressuser1;
  REVOKE SELECT ON atest4 FROM regressuser3; -- does nothing
  SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- true

pgsql-patches by date:

Previous
From: Andrew Dunstan
Date:
Subject: plperl / locale / win32
Next
From: Stephen Frost
Date:
Subject: pg_restore COPY error handling