OSDN Git Service

Fix permission-checking bug reported by Tim Burgess 10-Feb-03 (this time
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Jan 2004 23:01:55 +0000 (23:01 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Jan 2004 23:01:55 +0000 (23:01 +0000)
for sure...).  Rather than relying on the query context of a rangetable
entry to identify what permissions it wants checked, store a full AclMode
mask in each RTE, and check exactly those bits.  This allows an RTE
specifying, say, INSERT privilege on a view to be copied into a derived
UPDATE query without changing meaning.  Per recent discussion thread.
initdb forced due to change of stored rule representation.

18 files changed:
src/backend/commands/view.c
src/backend/executor/execMain.c
src/backend/executor/nodeSubplan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_relation.c
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/include/catalog/catversion.h
src/include/executor/executor.h
src/include/nodes/parsenodes.h
src/include/parser/parse_clause.h
src/include/utils/acl.h

index 346867b..a8c3cb5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.80 2004/01/10 23:28:44 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.81 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -297,8 +297,8 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
                                                                                          makeAlias("*NEW*", NIL),
                                                                                          false, false);
        /* Must override addRangeTableEntry's default access-check flags */
-       rt_entry1->checkForRead = false;
-       rt_entry2->checkForRead = false;
+       rt_entry1->requiredPerms = 0;
+       rt_entry2->requiredPerms = 0;
 
        new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
index 9d64c97..a340477 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.226 2004/01/10 23:28:44 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.227 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,8 +86,8 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
                   EState *estate);
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
-static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
-static void ExecCheckXactReadOnly(Query *parsetree, CmdType operation);
+static void ExecCheckRTEPerms(RangeTblEntry *rte);
+static void ExecCheckXactReadOnly(Query *parsetree);
 static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
                                  evalPlanQual *priorepq);
 static void EvalPlanQualStop(evalPlanQual *epq);
@@ -136,8 +136,8 @@ ExecutorStart(QueryDesc *queryDesc, bool useCurrentSnapshot, bool explainOnly)
         * If the transaction is read-only, we need to check if any writes are
         * planned to non-temporary tables.
         */
-       if (!explainOnly)
-               ExecCheckXactReadOnly(queryDesc->parsetree, queryDesc->operation);
+       if (XactReadOnly && !explainOnly)
+               ExecCheckXactReadOnly(queryDesc->parsetree);
 
        /*
         * Build EState, switch into per-query memory context for startup.
@@ -351,7 +351,7 @@ ExecutorRewind(QueryDesc *queryDesc)
  *             Check access permissions for all relations listed in a range table.
  */
 void
-ExecCheckRTPerms(List *rangeTable, CmdType operation)
+ExecCheckRTPerms(List *rangeTable)
 {
        List       *lp;
 
@@ -359,7 +359,7 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation)
        {
                RangeTblEntry *rte = lfirst(lp);
 
-               ExecCheckRTEPerms(rte, operation);
+               ExecCheckRTEPerms(rte);
        }
 }
 
@@ -368,18 +368,18 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation)
  *             Check access permissions for a single RTE.
  */
 static void
-ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
+ExecCheckRTEPerms(RangeTblEntry *rte)
 {
+       AclMode         requiredPerms;
        Oid                     relOid;
        AclId           userid;
-       AclResult       aclcheck_result;
 
        /*
         * If it's a subquery, recursively examine its rangetable.
         */
        if (rte->rtekind == RTE_SUBQUERY)
        {
-               ExecCheckRTPerms(rte->subquery->rtable, operation);
+               ExecCheckRTPerms(rte->subquery->rtable);
                return;
        }
 
@@ -391,6 +391,13 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
        if (rte->rtekind != RTE_RELATION)
                return;
 
+       /*
+        * No work if requiredPerms is empty.
+        */
+       requiredPerms = rte->requiredPerms;
+       if (requiredPerms == 0)
+               return;
+
        relOid = rte->relid;
 
        /*
@@ -404,77 +411,68 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
         */
        userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
 
-#define CHECK(MODE)            pg_class_aclcheck(relOid, userid, MODE)
+       /*
+        * For each bit in requiredPerms, apply the required check.  (We can't
+        * do this in one aclcheck call because aclcheck treats multiple bits
+        * as OR semantics, when we want AND.)
+        *
+        * We use a well-known cute trick for isolating the rightmost one-bit
+        * in a nonzero word.  See nodes/bitmapset.c for commentary.
+        */
+#define RIGHTMOST_ONE(x) ((int32) (x) & -((int32) (x)))
 
-       if (rte->checkForRead)
+       while (requiredPerms != 0)
        {
-               aclcheck_result = CHECK(ACL_SELECT);
-               if (aclcheck_result != ACLCHECK_OK)
-                       aclcheck_error(aclcheck_result, ACL_KIND_CLASS,
-                                                  get_rel_name(relOid));
-       }
+               AclMode         thisPerm;
+               AclResult       aclcheck_result;
 
-       if (rte->checkForWrite)
-       {
-               /*
-                * Note: write access in a SELECT context means SELECT FOR UPDATE.
-                * Right now we don't distinguish that from true update as far as
-                * permissions checks are concerned.
-                */
-               switch (operation)
-               {
-                       case CMD_INSERT:
-                               aclcheck_result = CHECK(ACL_INSERT);
-                               break;
-                       case CMD_SELECT:
-                       case CMD_UPDATE:
-                               aclcheck_result = CHECK(ACL_UPDATE);
-                               break;
-                       case CMD_DELETE:
-                               aclcheck_result = CHECK(ACL_DELETE);
-                               break;
-                       default:
-                               elog(ERROR, "unrecognized operation code: %d",
-                                        (int) operation);
-                               aclcheck_result = ACLCHECK_OK;  /* keep compiler quiet */
-                               break;
-               }
+               thisPerm = RIGHTMOST_ONE(requiredPerms);
+               requiredPerms &= ~thisPerm;
+
+               aclcheck_result = pg_class_aclcheck(relOid, userid, thisPerm);
                if (aclcheck_result != ACLCHECK_OK)
                        aclcheck_error(aclcheck_result, ACL_KIND_CLASS,
                                                   get_rel_name(relOid));
        }
 }
 
+/*
+ * Check that the query does not imply any writes to non-temp tables.
+ */
 static void
-ExecCheckXactReadOnly(Query *parsetree, CmdType operation)
+ExecCheckXactReadOnly(Query *parsetree)
 {
-       if (!XactReadOnly)
-               return;
+       List       *lp;
 
-       /* CREATE TABLE AS or SELECT INTO */
-       if (operation == CMD_SELECT && parsetree->into != NULL)
+       /*
+        * CREATE TABLE AS or SELECT INTO?
+        *
+        * XXX should we allow this if the destination is temp?
+        */
+       if (parsetree->into != NULL)
                goto fail;
 
-       if (operation == CMD_DELETE || operation == CMD_INSERT
-               || operation == CMD_UPDATE)
+       /* Fail if write permissions are requested on any non-temp table */
+       foreach(lp, parsetree->rtable)
        {
-               List       *lp;
+               RangeTblEntry *rte = lfirst(lp);
 
-               foreach(lp, parsetree->rtable)
+               if (rte->rtekind == RTE_SUBQUERY)
                {
-                       RangeTblEntry *rte = lfirst(lp);
+                       ExecCheckXactReadOnly(rte->subquery);
+                       continue;
+               }
 
-                       if (rte->rtekind != RTE_RELATION)
-                               continue;
+               if (rte->rtekind != RTE_RELATION)
+                       continue;
 
-                       if (!rte->checkForWrite)
-                               continue;
+               if ((rte->requiredPerms & (~ACL_SELECT)) == 0)
+                       continue;
 
-                       if (isTempNamespace(get_rel_namespace(rte->relid)))
-                               continue;
+               if (isTempNamespace(get_rel_namespace(rte->relid)))
+                       continue;
 
-                       goto fail;
-               }
+               goto fail;
        }
 
        return;
@@ -511,7 +509,7 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
         * rangetable here --- subplan RTEs will be checked during
         * ExecInitSubPlan().
         */
-       ExecCheckRTPerms(parseTree->rtable, operation);
+       ExecCheckRTPerms(parseTree->rtable);
 
        /*
         * get information from query descriptor
index da7d591..1624f41 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.59 2003/11/29 19:51:48 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.60 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -670,10 +670,9 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
        MemoryContext oldcontext;
 
        /*
-        * Do access checking on the rangetable entries in the subquery. Here,
-        * we assume the subquery is a SELECT.
+        * Do access checking on the rangetable entries in the subquery.
         */
-       ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
+       ExecCheckRTPerms(subplan->rtable);
 
        /*
         * initialize my state
index 5a340eb..39f454f 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.276 2004/01/10 23:28:44 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.277 2004/01/14 23:01:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1258,8 +1258,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
        COPY_NODE_FIELD(eref);
        COPY_SCALAR_FIELD(inh);
        COPY_SCALAR_FIELD(inFromCl);
-       COPY_SCALAR_FIELD(checkForRead);
-       COPY_SCALAR_FIELD(checkForWrite);
+       COPY_SCALAR_FIELD(requiredPerms);
        COPY_SCALAR_FIELD(checkAsUser);
 
        return newnode;
index 932d79f..7951fad 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.214 2004/01/10 23:28:45 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.215 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1574,8 +1574,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
        COMPARE_NODE_FIELD(eref);
        COMPARE_SCALAR_FIELD(inh);
        COMPARE_SCALAR_FIELD(inFromCl);
-       COMPARE_SCALAR_FIELD(checkForRead);
-       COMPARE_SCALAR_FIELD(checkForWrite);
+       COMPARE_SCALAR_FIELD(requiredPerms);
        COMPARE_SCALAR_FIELD(checkAsUser);
 
        return true;
index ce80cae..cd1fde7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.229 2004/01/06 04:31:01 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.230 2004/01/14 23:01:55 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1358,9 +1358,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 
        WRITE_BOOL_FIELD(inh);
        WRITE_BOOL_FIELD(inFromCl);
-       WRITE_BOOL_FIELD(checkForRead);
-       WRITE_BOOL_FIELD(checkForWrite);
-       WRITE_OID_FIELD(checkAsUser);
+       WRITE_UINT_FIELD(requiredPerms);
+       WRITE_UINT_FIELD(checkAsUser);
 }
 
 static void
index bbfcb1b..93c71fd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.164 2004/01/07 18:56:26 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.165 2004/01/14 23:01:55 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -939,9 +939,8 @@ _readRangeTblEntry(void)
 
        READ_BOOL_FIELD(inh);
        READ_BOOL_FIELD(inFromCl);
-       READ_BOOL_FIELD(checkForRead);
-       READ_BOOL_FIELD(checkForWrite);
-       READ_OID_FIELD(checkAsUser);
+       READ_UINT_FIELD(requiredPerms);
+       READ_UINT_FIELD(checkAsUser);
 
        READ_DONE();
 }
index 2d72426..50d5006 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.111 2004/01/05 05:07:35 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.112 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -227,8 +227,7 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
         * it examines the parent's inheritlist entry.  There's no need to
         * check twice, so turn off access check bits in the original RTE.
         */
-       rte->checkForRead = false;
-       rte->checkForWrite = false;
+       rte->requiredPerms = 0;
 
        /*
         * Initialize to compute size estimates for whole inheritance tree
index 33f32c1..8962082 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.295 2004/01/11 04:58:17 neilc Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.296 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -472,7 +472,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
        /* set up range table with just the result rel */
        qry->resultRelation = setTargetTable(pstate, stmt->relation,
                                                          interpretInhOption(stmt->relation->inhOpt),
-                                                                                true);
+                                                                                true,
+                                                                                ACL_DELETE);
 
        qry->distinctClause = NIL;
 
@@ -539,7 +540,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
         * table is not added to the joinlist or namespace.
         */
        qry->resultRelation = setTargetTable(pstate, stmt->relation,
-                                                                                false, false);
+                                                                                false, false, ACL_INSERT);
 
        /*
         * Is it INSERT ... SELECT or INSERT ... VALUES?
@@ -1721,8 +1722,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
                                                                makeAlias("*NEW*", NIL),
                                                                false, true);
        /* Must override addRangeTableEntry's default access-check flags */
-       oldrte->checkForRead = false;
-       newrte->checkForRead = false;
+       oldrte->requiredPerms = 0;
+       newrte->requiredPerms = 0;
 
        /*
         * They must be in the namespace too for lookup purposes, but only add
@@ -1820,8 +1821,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
                        newrte = addRangeTableEntry(sub_pstate, stmt->relation,
                                                                                makeAlias("*NEW*", NIL),
                                                                                false, false);
-                       oldrte->checkForRead = false;
-                       newrte->checkForRead = false;
+                       oldrte->requiredPerms = 0;
+                       newrte->requiredPerms = 0;
                        addRTEtoQuery(sub_pstate, oldrte, false, true);
                        addRTEtoQuery(sub_pstate, newrte, false, true);
 
@@ -2493,7 +2494,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 
        qry->resultRelation = setTargetTable(pstate, stmt->relation,
                                                          interpretInhOption(stmt->relation->inhOpt),
-                                                                                true);
+                                                                                true,
+                                                                                ACL_UPDATE);
 
        /*
         * the FROM clause is non-standard SQL syntax. We used to be able to
@@ -2880,7 +2882,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                                case RTE_RELATION:
                                        if (!intMember(i, rowMarks))    /* avoid duplicates */
                                                rowMarks = lappendi(rowMarks, i);
-                                       rte->checkForWrite = true;
+                                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                                        break;
                                case RTE_SUBQUERY:
                                        /*
@@ -2915,7 +2917,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                                                case RTE_RELATION:
                                                        if (!intMember(i, rowMarks)) /* avoid duplicates */
                                                                rowMarks = lappendi(rowMarks, i);
-                                                       rte->checkForWrite = true;
+                                                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                                                        break;
                                                case RTE_SUBQUERY:
                                                        /*
index d4e6747..8b7be43 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.125 2003/11/29 19:51:51 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.126 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,11 +116,14 @@ transformFromClause(ParseState *pstate, List *frmList)
  *       to check for namespace conflict; we assume that the namespace was
  *       initially empty in these cases.)
  *
+ *       Finally, we mark the relation as requiring the permissions specified
+ *       by requiredPerms.
+ *
  *       Returns the rangetable index of the target relation.
  */
 int
 setTargetTable(ParseState *pstate, RangeVar *relation,
-                          bool inh, bool alsoSource)
+                          bool inh, bool alsoSource, AclMode requiredPerms)
 {
        RangeTblEntry *rte;
        int                     rtindex;
@@ -149,16 +152,15 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
        Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
 
        /*
-        * Override addRangeTableEntry's default checkForRead, and instead
-        * mark target table as requiring write access.
+        * Override addRangeTableEntry's default ACL_SELECT permissions check,
+        * and instead mark target table as requiring exactly the specified
+        * permissions.
         *
         * If we find an explicit reference to the rel later during parse
-        * analysis, scanRTEForColumn will change checkForRead to 'true'
-        * again.  That can't happen for INSERT but it is possible for UPDATE
-        * and DELETE.
+        * analysis, scanRTEForColumn will add the ACL_SELECT bit back again.
+        * That can't happen for INSERT but it is possible for UPDATE and DELETE.
         */
-       rte->checkForRead = false;
-       rte->checkForWrite = true;
+       rte->requiredPerms = requiredPerms;
 
        /*
         * If UPDATE/DELETE, add table to joinlist and namespace.
index 76caa60..3e314be 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.91 2003/11/29 19:51:52 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -437,7 +437,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
  * nothing.  It might seem that we need to propagate the mark to all the
  * contained RTEs, but that is not necessary.  This is so because a join
  * expression can only appear in a FROM clause, and any table named in
- * FROM will be marked checkForRead from the beginning.
+ * FROM will be marked as requiring read access from the beginning.
  */
 static Node *
 scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -477,7 +477,8 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
                                                 errmsg("column reference \"%s\" is ambiguous",
                                                                colname)));
                        result = (Node *) make_var(pstate, rte, attnum);
-                       rte->checkForRead = true;
+                       /* Require read access */
+                       rte->requiredPerms |= ACL_SELECT;
                }
        }
 
@@ -504,7 +505,8 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
                                                                         0, 0))
                        {
                                result = (Node *) make_var(pstate, rte, attnum);
-                               rte->checkForRead = true;
+                               /* Require read access */
+                               rte->requiredPerms |= ACL_SELECT;
                        }
                }
        }
@@ -689,7 +691,7 @@ addRangeTableEntry(ParseState *pstate,
         * Flags:
         * - this RTE should be expanded to include descendant tables,
         * - this RTE is in the FROM clause,
-        * - this RTE should be checked for read/write access rights.
+        * - this RTE should be checked for appropriate access rights.
         *
         * The initial default on access checks is always check-for-READ-access,
         * which is the right thing for all except target tables.
@@ -697,10 +699,9 @@ addRangeTableEntry(ParseState *pstate,
         */
        rte->inh = inh;
        rte->inFromCl = inFromCl;
-       rte->checkForRead = true;
-       rte->checkForWrite = false;
 
-       rte->checkAsUser = InvalidOid;          /* not set-uid by default, either */
+       rte->requiredPerms = ACL_SELECT;
+       rte->checkAsUser = 0;                   /* not set-uid by default, either */
 
        /*
         * Add completed RTE to pstate's range table list, but not to join
@@ -784,7 +785,7 @@ addRangeTableEntryForRelation(ParseState *pstate,
         * Flags:
         * - this RTE should be expanded to include descendant tables,
         * - this RTE is in the FROM clause,
-        * - this RTE should be checked for read/write access rights.
+        * - this RTE should be checked for appropriate access rights.
         *
         * The initial default on access checks is always check-for-READ-access,
         * which is the right thing for all except target tables.
@@ -792,10 +793,9 @@ addRangeTableEntryForRelation(ParseState *pstate,
         */
        rte->inh = inh;
        rte->inFromCl = inFromCl;
-       rte->checkForRead = true;
-       rte->checkForWrite = false;
 
-       rte->checkAsUser = InvalidOid;          /* not set-uid by default, either */
+       rte->requiredPerms = ACL_SELECT;
+       rte->checkAsUser = 0;                   /* not set-uid by default, either */
 
        /*
         * Add completed RTE to pstate's range table list, but not to join
@@ -864,17 +864,16 @@ addRangeTableEntryForSubquery(ParseState *pstate,
         * Flags:
         * - this RTE should be expanded to include descendant tables,
         * - this RTE is in the FROM clause,
-        * - this RTE should be checked for read/write access rights.
+        * - this RTE should be checked for appropriate access rights.
         *
         * Subqueries are never checked for access rights.
         *----------
         */
        rte->inh = false;                       /* never true for subqueries */
        rte->inFromCl = inFromCl;
-       rte->checkForRead = false;
-       rte->checkForWrite = false;
 
-       rte->checkAsUser = InvalidOid;
+       rte->requiredPerms = 0;
+       rte->checkAsUser = 0;
 
        /*
         * Add completed RTE to pstate's range table list, but not to join
@@ -1034,15 +1033,17 @@ addRangeTableEntryForFunction(ParseState *pstate,
         * Flags:
         * - this RTE should be expanded to include descendant tables,
         * - this RTE is in the FROM clause,
-        * - this RTE should be checked for read/write access rights.
+        * - this RTE should be checked for appropriate access rights.
+        *
+        * Functions are never checked for access rights (at least, not by
+        * the RTE permissions mechanism).
         *----------
         */
        rte->inh = false;                       /* never true for functions */
        rte->inFromCl = inFromCl;
-       rte->checkForRead = true;
-       rte->checkForWrite = false;
 
-       rte->checkAsUser = InvalidOid;
+       rte->requiredPerms = 0;
+       rte->checkAsUser = 0;
 
        /*
         * Add completed RTE to pstate's range table list, but not to join
@@ -1095,17 +1096,16 @@ addRangeTableEntryForJoin(ParseState *pstate,
         * Flags:
         * - this RTE should be expanded to include descendant tables,
         * - this RTE is in the FROM clause,
-        * - this RTE should be checked for read/write access rights.
+        * - this RTE should be checked for appropriate access rights.
         *
         * Joins are never checked for access rights.
         *----------
         */
        rte->inh = false;                       /* never true for joins */
        rte->inFromCl = inFromCl;
-       rte->checkForRead = false;
-       rte->checkForWrite = false;
 
-       rte->checkAsUser = InvalidOid;
+       rte->requiredPerms = 0;
+       rte->checkAsUser = 0;
 
        /*
         * Add completed RTE to pstate's range table list, but not to join
index 90497cf..f1cbe96 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.91 2003/11/29 19:51:55 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@
 
 
 static void setRuleCheckAsUser(Query *qry, AclId userid);
-static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
+static bool setRuleCheckAsUser_walker(Node *node, AclId *context);
 
 
 /*
@@ -494,8 +494,8 @@ DefineQueryRewrite(RuleStmt *stmt)
  * Note: for a view (ON SELECT rule), the checkAsUser field of the *OLD*
  * RTE entry will be overridden when the view rule is expanded, and the
  * checkAsUser field of the *NEW* entry is irrelevant because that entry's
- * checkFor bits will never be set.  However, for other types of rules it's
- * important to set these fields to match the rule owner.  So we just set
+ * requiredPerms bits will always be zero.  However, for other types of rules
+ * it's important to set these fields to match the rule owner.  So we just set
  * them always.
  */
 static void
@@ -528,7 +528,7 @@ setRuleCheckAsUser(Query *qry, AclId userid)
  * Expression-tree walker to find sublink queries
  */
 static bool
-setRuleCheckAsUser_walker(Node *node, Oid *context)
+setRuleCheckAsUser_walker(Node *node, AclId *context)
 {
        if (node == NULL)
                return false;
index 3f69110..e66eb90 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.132 2004/01/14 03:39:22 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.133 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -655,13 +655,11 @@ ApplyRetrieveRule(Query *parsetree,
         */
        subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
        Assert(subrte->relid == relation->rd_id);
-       subrte->checkForRead = rte->checkForRead;
-       subrte->checkForWrite = rte->checkForWrite;
+       subrte->requiredPerms = rte->requiredPerms;
        subrte->checkAsUser = rte->checkAsUser;
 
-       rte->checkForRead = false;      /* no permission check on subquery itself */
-       rte->checkForWrite = false;
-       rte->checkAsUser = InvalidOid;
+       rte->requiredPerms = 0;         /* no permission check on subquery itself */
+       rte->checkAsUser = 0;
 
        /*
         * FOR UPDATE of view?
@@ -713,7 +711,7 @@ markQueryForUpdate(Query *qry, bool skipOldNew)
                {
                        if (!intMember(rti, qry->rowMarks))
                                qry->rowMarks = lappendi(qry->rowMarks, rti);
-                       rte->checkForWrite = true;
+                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                }
                else if (rte->rtekind == RTE_SUBQUERY)
                {
index 1910e33..7dd262c 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.215 2004/01/06 23:55:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.216 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200401061
+#define CATALOG_VERSION_NO     200401141
 
 #endif
index 8f1dc7f..0508947 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.104 2003/12/18 20:21:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.105 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,7 +91,7 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
                        ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
 extern void ExecutorRewind(QueryDesc *queryDesc);
-extern void ExecCheckRTPerms(List *rangeTable, CmdType operation);
+extern void ExecCheckRTPerms(List *rangeTable);
 extern void ExecEndPlan(PlanState *planstate, EState *estate);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
                                TupleTableSlot *slot, EState *estate);
index 8b6446d..01ff239 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.252 2004/01/10 23:28:45 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.253 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,32 @@ typedef enum QuerySource
        QSRC_NON_INSTEAD_RULE           /* added by non-INSTEAD rule */
 } QuerySource;
 
+/*
+ * Grantable rights are encoded so that we can OR them together in a bitmask.
+ * The present representation of AclItem limits us to 15 distinct rights,
+ * even though AclMode is defined as uint32.  See utils/acl.h.
+ *
+ * Caution: changing these codes breaks stored ACLs, hence forces initdb.
+ */
+typedef uint32 AclMode;                        /* a bitmask of privilege bits */
+
+#define ACL_INSERT             (1<<0)  /* for relations */
+#define ACL_SELECT             (1<<1)
+#define ACL_UPDATE             (1<<2)
+#define ACL_DELETE             (1<<3)
+#define ACL_RULE               (1<<4)
+#define ACL_REFERENCES (1<<5)
+#define ACL_TRIGGER            (1<<6)
+#define ACL_EXECUTE            (1<<7)  /* for functions */
+#define ACL_USAGE              (1<<8)  /* for languages and namespaces */
+#define ACL_CREATE             (1<<9)  /* for namespaces and databases */
+#define ACL_CREATE_TEMP (1<<10) /* for databases */
+#define N_ACL_RIGHTS   11              /* 1 plus the last 1<<x */
+#define ACL_ALL_RIGHTS (-1)    /* all-privileges marker in GRANT list */
+#define ACL_NO_RIGHTS  0
+/* Currently, SELECT ... FOR UPDATE requires UPDATE privileges */
+#define ACL_SELECT_FOR_UPDATE  ACL_UPDATE
+
 
 /*****************************************************************************
  *     Query Tree
@@ -425,12 +451,13 @@ typedef struct DefElem
  *       column names processed later, and it also shouldn't affect the
  *       expansion of '*'.
  *
- *       checkForRead, checkForWrite, and checkAsUser control run-time access
- *       permissions checks.  A rel will be checked for read or write access
- *       (or both, or neither) per checkForRead and checkForWrite.  If
- *       checkAsUser is not InvalidOid, then do the permissions checks using
- *       the access rights of that user, not the current effective user ID.
- *       (This allows rules to act as setuid gateways.)
+ *       requiredPerms and checkAsUser specify run-time access permissions
+ *       checks to be performed at query startup.  The user must have *all*
+ *       of the permissions that are OR'd together in requiredPerms (zero
+ *       indicates no permissions checking).  If checkAsUser is not zero,
+ *       then do the permissions checks using the access rights of that user,
+ *       not the current effective user ID.  (This allows rules to act as
+ *       setuid gateways.)
  *--------------------
  */
 typedef enum RTEKind
@@ -490,9 +517,8 @@ typedef struct RangeTblEntry
        Alias      *eref;                       /* expanded reference names */
        bool            inh;                    /* inheritance requested? */
        bool            inFromCl;               /* present in FROM clause */
-       bool            checkForRead;   /* check rel for read access */
-       bool            checkForWrite;  /* check rel for write access */
-       Oid                     checkAsUser;    /* if not zero, check access as this user */
+       AclMode         requiredPerms;  /* bitmask of required access permissions */
+       AclId           checkAsUser;    /* if not zero, check access as this user */
 } RangeTblEntry;
 
 /*
@@ -809,26 +835,6 @@ typedef enum GrantObjectType
        ACL_OBJECT_NAMESPACE            /* namespace */
 } GrantObjectType;
 
-/*
- * Grantable rights are encoded so that we can OR them together in a bitmask.
- * The present representation of AclItem limits us to 15 distinct rights.
- * Caution: changing these codes breaks stored ACLs, hence forces initdb.
- */
-#define ACL_INSERT             (1<<0)  /* for relations */
-#define ACL_SELECT             (1<<1)
-#define ACL_UPDATE             (1<<2)
-#define ACL_DELETE             (1<<3)
-#define ACL_RULE               (1<<4)
-#define ACL_REFERENCES (1<<5)
-#define ACL_TRIGGER            (1<<6)
-#define ACL_EXECUTE            (1<<7)  /* for functions */
-#define ACL_USAGE              (1<<8)  /* for languages and namespaces */
-#define ACL_CREATE             (1<<9)  /* for namespaces and databases */
-#define ACL_CREATE_TEMP (1<<10) /* for databases */
-#define N_ACL_RIGHTS   11              /* 1 plus the last 1<<x */
-#define ACL_ALL_RIGHTS (-1)    /* all-privileges marker in GRANT list */
-#define ACL_NO_RIGHTS  0
-
 typedef struct GrantStmt
 {
        NodeTag         type;
index a57a803..d91f5e8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_clause.h,v 1.38 2003/11/29 22:41:09 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_clause.h,v 1.39 2004/01/14 23:01:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,7 @@
 
 extern void transformFromClause(ParseState *pstate, List *frmList);
 extern int setTargetTable(ParseState *pstate, RangeVar *relation,
-                          bool inh, bool alsoSource);
+                          bool inh, bool alsoSource, AclMode requiredPerms);
 extern bool interpretInhOption(InhOption inhOpt);
 
 extern Node *transformWhereClause(ParseState *pstate, Node *clause,
index be34fcc..efe7af3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.65 2003/11/29 22:41:15 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.66 2004/01/14 23:01:55 tgl Exp $
  *
  * NOTES
  *       An ACL array is simply an array of AclItems, representing the union
 #include "utils/array.h"
 
 
-/* typedef AclId is declared in c.h */
+/*
+ * typedef AclId is declared in c.h
+ *
+ * typedef AclMode is declared in parsenodes.h, also the individual privilege
+ * bit meanings are defined there
+ */
 
 #define ACL_ID_WORLD   0               /* placeholder for id in a WORLD acl item */
 
 #define ACL_IDTYPE_GID                 0x02    /* group id - from pg_group */
 
 /*
- * AclMode             a bitmask of privilege bits
- */
-typedef uint32 AclMode;
-
-/*
  * AclItem
  *
  * The IDTYPE included in ai_privs identifies the type of the grantee ID.