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

From Bruce Momjian
Subject Re: [HACKERS] Inconsistent syntax in GRANT
Date
Msg-id 200601100124.k0A1OCD19881@candle.pha.pa.us
Whole thread Raw
In response to Re: [HACKERS] Inconsistent syntax in GRANT  (Bruce Momjian <pgman@candle.pha.pa.us>)
Responses Re: [HACKERS] Inconsistent syntax in GRANT
List pgsql-patches
Bruce Momjian wrote:
> Tom Lane wrote:
> > I wrote:
> > > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > >> Does the standard require USAGE to support currval?
> >
> > > currval isn't in the standard (unless I missed something), so it has
> > > nothing to say one way or the other on the point.
> >
> > Wait, I take that back.  Remember our previous discussions about this
> > point: the spec's NEXT VALUE FOR construct is *not* equivalent to
> > nextval, because they specify that the sequence advances just once per
> > command even if the command says NEXT VALUE FOR in multiple places.
> > This means that NEXT VALUE FOR is effectively both nextval and currval;
> > the first one in a command does nextval and the rest do currval.
> >
> > Accordingly, I think it's reasonable to read the spec as saying that
> > USAGE privilege encompasses both nextval and currval.
>
> Here's a patch that more closely matches the ideas proposed.

Here is an updated patch.  I hit a few issues.

At first I was just going to continue allowing table-like permissions
for sequences if a GRANT [TABLE] was used, and add the new
USAGE/SELECT/UPDATE capability only for GRANT SEQUENCE.  The problem was
that you could create a non-dumpable permission setup if you added
DELETE permission to a sequence using GRANT TABLE, and USAGE permission
using GRANT SEQUENCE.  That couldn't be dumped with TABLE or with
SEQUENCE, and I didn't want to do a double-dump of GRANT to fit that,
nor did I want to throw an warning during the dump run.

What I did was to throw a warning if an invalid permission is specified
for a sequence in GRANT TABLE.  By doing this, un-dumpable permission
combinations will not be loaded into an 8.2 database.  (GRANT ALL ON
TABLE sets the sequence-only permissions.)

    test=> GRANT DELETE ON seq TO PUBLIC;
    WARNING:  invalid privilege type DELETE for sequence
    WARNING:  no privileges were granted
    GRANT

    test=> GRANT DELETE,SELECT  ON seq TO PUBLIC;
    WARNING:  invalid privilege type DELETE for sequence
    GRANT

This seemed the safest backward-compatible setup.  It will have to be
mentioned in the release notes so users know they might get warnings
from loading sequences into 8.2.

You might think that it is unlikely for a DELETE permission to be
assigned to a sequences, but a simple GRANT ALL and REVOKE INSERT in 8.1
will cause:

    test=> CREATE TABLE tab(x INTEGER);
    CREATE TABLE
    test=> GRANT ALL ON tab TO PUBLIC;
    GRANT
    test=> REVOKE INSERT ON tab FROM PUBLIC;
    REVOKE

yields in pg_dump output:

    GRANT SELECT,RULE,UPDATE,DELETE,REFERENCES,TRIGGER ON TABLE tab
        TO PUBLIC;

This test was done on a table, but in 8.1 the same would appear for a
sequence with these warnings on load into 8.2:

    WARNING:  invalid privilege type RULE for sequence
    WARNING:  invalid privilege type DELETE for sequence
    WARNING:  invalid privilege type REFERENCES for sequence
    WARNING:  invalid privilege type TRIGGER for sequence
    GRANT

Another tricky case was this:

    test=> GRANT DELETE ON tab, seq TO PUBLIC;

GRANT allows multiple objects to be listed, as illustrated above.  The
current code checks for valid permissions in one place because it
assumes all listed objects are of the same type and accept the same
permissions.  Because GRANT TABLE must allow only valid permissions for
sequences (to avoid un-dumpable output) I had to throw an error if a
sequence is mixed with a non-sequence, and the permission did not apply
to both sequences and non-sequences, rather than throw a warning like I
usually do for invalid sequence permissions:

    test=> REVOKE DELETE ON seq, tab FROM PUBLIC;
    WARNING:  invalid privilege type DELETE for sequence
    ERROR:  DELETE privilege invalid for command mixing sequences and non-sequences

    test=> REVOKE SELECT ON tab, seq FROM PUBLIC;
    REVOKE

Because allowing sequences to use GRANT TABLE is only for backward
compatibility, I think this is fine.  If not, we would have to split
apart the permission checking for tables from the existing routine, and
lose modularity in the code.

This patch also contains Marko's documentation adjustments.

Would someone look at the change in src/backend/catalog/pg_shdepend.c
for shared dependencies?  We don't have any system catalog sequences let
alone any shared catalog sequences, so I assume we are OK with assuming
it is a relation.  I added a comment just in case.

--
  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    10 Jan 2006 01:18:32 -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    10 Jan 2006 01:18:32 -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    10 Jan 2006 01:18:34 -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;
***************
*** 277,319 ****
                              get_roleid_checked(grantee->rolname));
      }

-     /*
-      * Convert stmt->privileges, a textual list, into an AclMode bitmask.
-      */
-     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;
-             errormsg = _("invalid privilege type %s for database");
-             break;
-         case ACL_OBJECT_FUNCTION:
-             all_privileges = ACL_ALL_RIGHTS_FUNCTION;
-             errormsg = _("invalid privilege type %s for function");
-             break;
-         case ACL_OBJECT_LANGUAGE:
-             all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
-             errormsg = _("invalid privilege type %s for language");
-             break;
-         case ACL_OBJECT_NAMESPACE:
-             all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
-             errormsg = _("invalid privilege type %s for namespace");
-             break;
-         case ACL_OBJECT_TABLESPACE:
-             all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
-             errormsg = _("invalid privilege type %s for tablespace");
-             break;
-         default:
-             /* keep compiler quiet */
-             all_privileges = ACL_NO_RIGHTS;
-             errormsg = NULL;
-             elog(ERROR, "unrecognized GrantStmt.objtype: %d",
-                  (int) stmt->objtype);
-     }
-
      if (stmt->privileges == NIL)
      {
          istmt.all_privs = true;
--- 280,285 ----
***************
*** 327,343 ****
      {
          istmt.all_privs = false;
          istmt.privileges = ACL_NO_RIGHTS;
          foreach(cell, stmt->privileges)
          {
              char       *privname = strVal(lfirst(cell));
              AclMode        priv = string_to_privilege(privname);

!             if (priv & ~((AclMode) all_privileges))
!                 ereport(ERROR,
!                         (errcode(ERRCODE_INVALID_GRANT_OPERATION),
!                          errmsg(errormsg,
                                  privilege_to_string(priv))));
!
              istmt.privileges |= priv;
          }
      }
--- 293,429 ----
      {
          istmt.all_privs = false;
          istmt.privileges = ACL_NO_RIGHTS;
+
          foreach(cell, stmt->privileges)
          {
              char       *privname = strVal(lfirst(cell));
              AclMode        priv = string_to_privilege(privname);

!             /*
!              *    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.
!              */
!             if (stmt->objtype == ACL_OBJECT_RELATION)
!             {
!                 ListCell   *cell2;
!                 bool        skip_priv = false;
!                 bool        non_seq_found = false;
!
!                 /*
!                  *    GRANT can have sequences and non-sequence objects
!                  *    in the same command, so loop over each object.
!                  */
!                 foreach(cell2, istmt.objects)
!                 {
!                     Oid            relOid = lfirst_oid(cell2);
!                     Form_pg_class pg_class_tuple;
!                     HeapTuple    tuple;
!
!                     tuple = SearchSysCache(RELOID,
!                                            ObjectIdGetDatum(relOid),
!                                            0, 0, 0);
!                     if (!HeapTupleIsValid(tuple))
!                         elog(ERROR, "cache lookup failed for relation %u", relOid);
!                     pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
!
!                     if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
!                     {
!                         all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
!                         errormsg = _("invalid privilege type %s for sequence");
!                         /*
!                          *    For backward compatibility, throw just a warning
!                          *    for invalid sequence permissions when using the
!                          *    non-sequence GRANT syntax is used.
!                          */
!                         if (priv & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
!                         {
!                             ereport(WARNING,
!                                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
!                                      errmsg(_("invalid privilege type %s for sequence"),
!                                         privilege_to_string(priv))));
!                             /* Skip assigning this priviledge */
!                             skip_priv = true;
!                         }
!                     }
!                     else
!                     {
!                         if (priv & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
!                             ereport(ERROR,
!                                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
!                                      errmsg(_("invalid privilege type %s for table"),
!                                         privilege_to_string(priv))));
!                         non_seq_found = true;
!                     }
!                     ReleaseSysCache(tuple);
!                 }
!                 /* If we get here, we have issued only warnings */
!                 if (skip_priv)
!                 {
!                     if (non_seq_found)
!                         /*
!                          *    If we get here, someone has issued a command like:
!                          *
!                          *        GRANT DELETE ON tab, seq TO PUBLIC
!                          *
!                          *    In thise case, the DELETE is valid for the table
!                          *    but not for the sequences.  We don't want to continue
!                          *    processing with a permission that will only partly
!                          *    succeed, so we ERROR.
!                          */
!                         ereport(ERROR,
!                             (errcode(ERRCODE_INVALID_GRANT_OPERATION),
!                              errmsg(_("%s privilege invalid for command mixing sequences and non-sequences"),
                                  privilege_to_string(priv))));
!                     priv = 0;
!                 }
!             }
!             else
!             {
!                 /*
!                  * Convert stmt->privileges, a textual list, into an AclMode bitmask.
!                  */
!                 switch (stmt->objtype)
!                 {
!                     /* ACL_OBJECT_RELATION:  handled above */
!                     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;
!                         errormsg = _("invalid privilege type %s for database");
!                         break;
!                     case ACL_OBJECT_FUNCTION:
!                         all_privileges = ACL_ALL_RIGHTS_FUNCTION;
!                         errormsg = _("invalid privilege type %s for function");
!                         break;
!                     case ACL_OBJECT_LANGUAGE:
!                         all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
!                         errormsg = _("invalid privilege type %s for language");
!                         break;
!                     case ACL_OBJECT_NAMESPACE:
!                         all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
!                         errormsg = _("invalid privilege type %s for namespace");
!                         break;
!                     case ACL_OBJECT_TABLESPACE:
!                         all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
!                         errormsg = _("invalid privilege type %s for tablespace");
!                         break;
!                     default:
!                         /* keep compiler quiet */
!                         all_privileges = ACL_NO_RIGHTS;
!                         errormsg = NULL;
!                         elog(ERROR, "unrecognized GrantStmt.objtype: %d",
!                              (int) stmt->objtype);
!                 }
!                 if (priv & ~((AclMode) all_privileges))
!                     ereport(ERROR,
!                             (errcode(ERRCODE_INVALID_GRANT_OPERATION),
!                              errmsg(errormsg,
!                                     privilege_to_string(priv))));
!             }
!
              istmt.privileges |= priv;
          }
      }
***************
*** 356,361 ****
--- 442,448 ----
      switch (istmt->objtype)
      {
          case ACL_OBJECT_RELATION:
+         case ACL_OBJECT_SEQUENCE:
              ExecGrant_Relation(istmt);
              break;
          case ACL_OBJECT_DATABASE:
***************
*** 395,400 ****
--- 482,488 ----
      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)
--- 611,625 ----
      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 ****
--- 665,689 ----
                       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;
+
          /*
           * 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);

--- 692,705 ----
          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));

          /*
--- 709,718 ----
           */
          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 ****
--- 1447,1454 ----
  {
      /* 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 ****
--- 1473,1480 ----
  {
      /* 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 ****
--- 1554,1560 ----
      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) &&
--- 1616,1624 ----
       *
       * 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);
      }

      /*
--- 1627,1633 ----
  #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
--- 1652,1660 ----
      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    10 Jan 2006 01:18:35 -0000
***************
*** 1133,1138 ****
--- 1133,1139 ----
                      switch (sdepForm->classid)
                      {
                          case RelationRelationId:
+                             /* could be a sequence? */
                              istmt.objtype = ACL_OBJECT_RELATION;
                              break;
                          case DatabaseRelationId:
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    10 Jan 2006 01:18:37 -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    10 Jan 2006 01:18:47 -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    10 Jan 2006 01:18:50 -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.23
diff -c -c -r1.23 dumputils.c
*** src/bin/pg_dump/dumputils.c    3 Dec 2005 21:06:18 -0000    1.23
--- src/bin/pg_dump/dumputils.c    10 Jan 2006 01:18:51 -0000
***************
*** 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);
--- 22,28 ----
  #define supports_grant_options(version) ((version) >= 70400)

  static bool parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion, bool *is_valid_for_sequence,
               PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo);
  static char *copyAclUserName(PQExpBuffer output, char *input);
***************
*** 395,404 ****
--- 395,416 ----
      /* Scan individual ACL items */
      for (i = 0; i < naclitems; i++)
      {
+         const char *outType = type;
+         bool    is_valid_for_sequence;
+
          if (!parseAclItem(aclitems[i], type, name, remoteVersion,
+                           &is_valid_for_sequence,
                            grantee, grantor, privs, privswgo))
              return false;

+         /*
+          *    For backward compatibility, non-SEQUENCE GRANT statements issue
+          *    warnings rather than errors for invalid sequence permissions.
+          *    This should only happen in pre-8.2 databases.
+          */
+         if (strcmp(outType, "SEQUENCE") == 0 && !is_valid_for_sequence)
+             outType = "TABLE";
+
          if (grantor->len == 0 && owner)
              printfPQExpBuffer(grantor, "%s", owner);

***************
*** 419,433 ****
                      : strcmp(privs->data, "ALL") != 0)
                  {
                      appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n",
!                                       type, name,
                                        fmtId(grantee->data));
                      if (privs->len > 0)
                          appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s;\n",
!                                           privs->data, type, name,
                                            fmtId(grantee->data));
                      if (privswgo->len > 0)
                          appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
!                                           privswgo->data, type, name,
                                            fmtId(grantee->data));
                  }
              }
--- 431,445 ----
                      : strcmp(privs->data, "ALL") != 0)
                  {
                      appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n",
!                                       outType, name,
                                        fmtId(grantee->data));
                      if (privs->len > 0)
                          appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s;\n",
!                                           privs->data, outType, name,
                                            fmtId(grantee->data));
                      if (privswgo->len > 0)
                          appendPQExpBuffer(firstsql, "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
!                                           privswgo->data, outType, name,
                                            fmtId(grantee->data));
                  }
              }
***************
*** 444,450 ****
                  if (privs->len > 0)
                  {
                      appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
!                                       privs->data, type, name);
                      if (grantee->len == 0)
                          appendPQExpBuffer(secondsql, "PUBLIC;\n");
                      else if (strncmp(grantee->data, "group ",
--- 456,462 ----
                  if (privs->len > 0)
                  {
                      appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
!                                       privs->data, outType, name);
                      if (grantee->len == 0)
                          appendPQExpBuffer(secondsql, "PUBLIC;\n");
                      else if (strncmp(grantee->data, "group ",
***************
*** 457,463 ****
                  if (privswgo->len > 0)
                  {
                      appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
!                                       privswgo->data, type, name);
                      if (grantee->len == 0)
                          appendPQExpBuffer(secondsql, "PUBLIC");
                      else if (strncmp(grantee->data, "group ",
--- 469,475 ----
                  if (privswgo->len > 0)
                  {
                      appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
!                                       privswgo->data, outType, name);
                      if (grantee->len == 0)
                          appendPQExpBuffer(secondsql, "PUBLIC");
                      else if (strncmp(grantee->data, "group ",
***************
*** 480,489 ****
       * If we didn't find any owner privs, the owner must have revoked 'em all
       */
      if (!found_owner_privs && owner)
-     {
          appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM %s;\n",
                            type, name, fmtId(owner));
-     }

      destroyPQExpBuffer(grantee);
      destroyPQExpBuffer(grantor);
--- 492,499 ----
***************
*** 517,523 ****
   */
  static bool
  parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion,
               PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo)
  {
--- 527,533 ----
   */
  static bool
  parseAclItem(const char *item, const char *type, const char *name,
!              int remoteVersion, bool *is_valid_for_sequence,
               PQExpBuffer grantee, PQExpBuffer grantor,
               PQExpBuffer privs, PQExpBuffer privswgo)
  {
***************
*** 530,535 ****
--- 540,547 ----

      buf = strdup(item);

+     *is_valid_for_sequence = true;
+
      /* user or group name is string up to = */
      eqpos = copyAclUserName(grantee, buf);
      if (*eqpos != '=')
***************
*** 547,554 ****
--- 559,572 ----
      else
          resetPQExpBuffer(grantor);

+     if (strcmp(type, "SEQUENCE") == 0 &&
+         /* SELECT, USAGE, UPDATE, ALL */
+         strspn(eqpos + 1, "rUw*") != strlen(eqpos + 1))
+         *is_valid_for_sequence = false;
+
      /* privilege codes */
  #define CONVERT_PRIV(code, keywd) \
+ do { \
      if ((pos = strchr(eqpos + 1, code))) \
      { \
          if (*(pos + 1) == '*') \
***************
*** 563,578 ****
          } \
      } \
      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)
          {
--- 581,599 ----
          } \
      } \
      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('a', "INSERT");
          CONVERT_PRIV('r', "SELECT");
          CONVERT_PRIV('R', "RULE");
+         if (strcmp(type, "SEQUENCE") == 0)
+             CONVERT_PRIV('U', "USAGE");

          if (remoteVersion >= 70200)
          {
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    10 Jan 2006 01:18:56 -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,6796 ----

          /* Handle the ACL here */
          namecopy = strdup(fmtId(tbinfo->dobj.name));
!         dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
!                 /* Issue GRANT SEQUENCE, if applicable */
!                 tbinfo->relkind != RELKIND_SEQUENCE ? "TABLE" : "SEQUENCE",
                  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    10 Jan 2006 01:18:58 -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    10 Jan 2006 01:18:58 -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 */

pgsql-patches by date:

Previous
From: Neil Conway
Date:
Subject: Re: pl/python refcount bug
Next
From: Tom Lane
Date:
Subject: Re: Proposed patch to change "missing FROM" messages