diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 346867bbfc4b59d945674ad0d6ede4c9ea32f817..a8c3cb5ce07ca2d220c0c8c2f295928d4bb2381e 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -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)); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 9d64c979e08706279b0f48c1bebf6d80350ad2fc..a340477c47ab36121e3b854dd86667e4afb07547 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -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 diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index da7d5915f20591b2976f908d2f7442cf0f22fdc1..1624f41fd540cf083cc241e467034f7f0262275a 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -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 diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5a340ebaa9493b3f2d641b7dde4cb718ec5f7476..39f454fd3c9d38350ff8f0e63a6719e1173523c7 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 932d79f31f48e1c1beaecf86175ee0295fac887b..7951fad039c629baf39ea0b7e18e5674d3cc5e38 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index ce80cae4bdbf8e6478a8f4a4be97d53596a0a708..cd1fde7b5b7ffcc2f0d11c23dd9081017ab284ac 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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 diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index bbfcb1b454eca204394a56eb7717493e613d244b..93c71fd224775e28ee8b51616bfba178d65b410b 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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(); } diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 2d724265f06dc698ec3a2fb0fbf9c89181aea881..50d5006a960d739cd2e4e1e0cbce6e0f292ec6c0 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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 diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 33f32c1b377fb6e3321aff44f22d484147f5dbc1..8962082134726540583f5dd8b863df082b5b4d8f 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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: /* diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index d4e6747df6ff441fed81692bc03fb682da91191e..8b7be43af13414729dee092f60b738b733bb12bf 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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. diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 76caa60aeb24ab0f1045493d488be03ddc7dd05b..3e314bea963417675f3d3044d3c69aaac422c70d 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -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 diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 90497cf5b88f8450c2dbef15f30d99ed70a85023..f1cbe96fd2a2c206a3356f992f496b070e7f1f25 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -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; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 3f69110a36a8d8093c12938449769dced2461ce5..e66eb905f563a198f485b9872e80ffb95eaea900 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 1910e33e3b112cb316e5eaea7ff1061830e62cef..7dd262c1e464f424a41acbfb4faf420a7dc3fd2b 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 8f1dc7fafaffb56001cb3ee98aa69eab1d23d61b..050894708c8bb57d104851eee87d4fa46d93beb3 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -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); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 8b6446d860518dd6ff2c004fbe5ac7c3d2e74fe5..01ff239a444548c9ffd915768f21c43d6cdb8dcd 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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; diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index a57a80325b54826c4a8a3ecc2d56b9a25671888a..d91f5e80643ae5af28ec2dd433161f4c698c7c44 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -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, diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index be34fcce5ce12776a019964279ba4d42bc6d328e..efe7af30b202934de8078c68db648b91ff71ae90 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -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 @@ -28,7 +28,12 @@ #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 */ @@ -39,11 +44,6 @@ #define ACL_IDTYPE_UID 0x01 /* user id - from pg_shadow */ #define ACL_IDTYPE_GID 0x02 /* group id - from pg_group */ -/* - * AclMode a bitmask of privilege bits - */ -typedef uint32 AclMode; - /* * AclItem *