2626 *
2727 *
2828 * IDENTIFICATION
29- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.226 2004/01/10 23:28:44 neilc Exp $
29+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.227 2004/01/14 23:01:54 tgl Exp $
3030 *
3131 *-------------------------------------------------------------------------
3232 */
@@ -86,8 +86,8 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
8686EState * estate );
8787static TupleTableSlot * EvalPlanQualNext (EState * estate );
8888static void EndEvalPlanQual (EState * estate );
89- static void ExecCheckRTEPerms (RangeTblEntry * rte , CmdType operation );
90- static void ExecCheckXactReadOnly (Query * parsetree , CmdType operation );
89+ static void ExecCheckRTEPerms (RangeTblEntry * rte );
90+ static void ExecCheckXactReadOnly (Query * parsetree );
9191static void EvalPlanQualStart (evalPlanQual * epq ,EState * estate ,
9292evalPlanQual * priorepq );
9393static void EvalPlanQualStop (evalPlanQual * epq );
@@ -136,8 +136,8 @@ ExecutorStart(QueryDesc *queryDesc, bool useCurrentSnapshot, bool explainOnly)
136136 * If the transaction is read-only, we need to check if any writes are
137137 * planned to non-temporary tables.
138138 */
139- if (!explainOnly )
140- ExecCheckXactReadOnly (queryDesc -> parsetree , queryDesc -> operation );
139+ if (XactReadOnly && !explainOnly )
140+ ExecCheckXactReadOnly (queryDesc -> parsetree );
141141
142142/*
143143 * Build EState, switch into per-query memory context for startup.
@@ -351,15 +351,15 @@ ExecutorRewind(QueryDesc *queryDesc)
351351 *Check access permissions for all relations listed in a range table.
352352 */
353353void
354- ExecCheckRTPerms (List * rangeTable , CmdType operation )
354+ ExecCheckRTPerms (List * rangeTable )
355355{
356356List * lp ;
357357
358358foreach (lp ,rangeTable )
359359{
360360RangeTblEntry * rte = lfirst (lp );
361361
362- ExecCheckRTEPerms (rte , operation );
362+ ExecCheckRTEPerms (rte );
363363}
364364}
365365
@@ -368,18 +368,18 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation)
368368 *Check access permissions for a single RTE.
369369 */
370370static void
371- ExecCheckRTEPerms (RangeTblEntry * rte , CmdType operation )
371+ ExecCheckRTEPerms (RangeTblEntry * rte )
372372{
373+ AclMode requiredPerms ;
373374Oid relOid ;
374375AclId userid ;
375- AclResult aclcheck_result ;
376376
377377/*
378378 * If it's a subquery, recursively examine its rangetable.
379379 */
380380if (rte -> rtekind == RTE_SUBQUERY )
381381{
382- ExecCheckRTPerms (rte -> subquery -> rtable , operation );
382+ ExecCheckRTPerms (rte -> subquery -> rtable );
383383return ;
384384}
385385
@@ -391,6 +391,13 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
391391if (rte -> rtekind != RTE_RELATION )
392392return ;
393393
394+ /*
395+ * No work if requiredPerms is empty.
396+ */
397+ requiredPerms = rte -> requiredPerms ;
398+ if (requiredPerms == 0 )
399+ return ;
400+
394401relOid = rte -> relid ;
395402
396403/*
@@ -404,77 +411,68 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
404411 */
405412userid = rte -> checkAsUser ?rte -> checkAsUser :GetUserId ();
406413
407- #define CHECK (MODE )pg_class_aclcheck(relOid, userid, MODE)
414+ /*
415+ * For each bit in requiredPerms, apply the required check. (We can't
416+ * do this in one aclcheck call because aclcheck treats multiple bits
417+ * as OR semantics, when we want AND.)
418+ *
419+ * We use a well-known cute trick for isolating the rightmost one-bit
420+ * in a nonzero word. See nodes/bitmapset.c for commentary.
421+ */
422+ #define RIGHTMOST_ONE (x ) ((int32) (x) & -((int32) (x)))
408423
409- if ( rte -> checkForRead )
424+ while ( requiredPerms != 0 )
410425{
411- aclcheck_result = CHECK (ACL_SELECT );
412- if (aclcheck_result != ACLCHECK_OK )
413- aclcheck_error (aclcheck_result ,ACL_KIND_CLASS ,
414- get_rel_name (relOid ));
415- }
426+ AclMode thisPerm ;
427+ AclResult aclcheck_result ;
416428
417- if (rte -> checkForWrite )
418- {
419- /*
420- * Note: write access in a SELECT context means SELECT FOR UPDATE.
421- * Right now we don't distinguish that from true update as far as
422- * permissions checks are concerned.
423- */
424- switch (operation )
425- {
426- case CMD_INSERT :
427- aclcheck_result = CHECK (ACL_INSERT );
428- break ;
429- case CMD_SELECT :
430- case CMD_UPDATE :
431- aclcheck_result = CHECK (ACL_UPDATE );
432- break ;
433- case CMD_DELETE :
434- aclcheck_result = CHECK (ACL_DELETE );
435- break ;
436- default :
437- elog (ERROR ,"unrecognized operation code: %d" ,
438- (int )operation );
439- aclcheck_result = ACLCHECK_OK ;/* keep compiler quiet */
440- break ;
441- }
429+ thisPerm = RIGHTMOST_ONE (requiredPerms );
430+ requiredPerms &= ~thisPerm ;
431+
432+ aclcheck_result = pg_class_aclcheck (relOid ,userid ,thisPerm );
442433if (aclcheck_result != ACLCHECK_OK )
443434aclcheck_error (aclcheck_result ,ACL_KIND_CLASS ,
444435get_rel_name (relOid ));
445436}
446437}
447438
439+ /*
440+ * Check that the query does not imply any writes to non-temp tables.
441+ */
448442static void
449- ExecCheckXactReadOnly (Query * parsetree , CmdType operation )
443+ ExecCheckXactReadOnly (Query * parsetree )
450444{
451- if (!XactReadOnly )
452- return ;
445+ List * lp ;
453446
454- /* CREATE TABLE AS or SELECT INTO */
455- if (operation == CMD_SELECT && parsetree -> into != NULL )
447+ /*
448+ * CREATE TABLE AS or SELECT INTO?
449+ *
450+ * XXX should we allow this if the destination is temp?
451+ */
452+ if (parsetree -> into != NULL )
456453gotofail ;
457454
458- if ( operation == CMD_DELETE || operation == CMD_INSERT
459- || operation == CMD_UPDATE )
455+ /* Fail ifwrite permissions are requested on any non-temp table */
456+ foreach ( lp , parsetree -> rtable )
460457{
461- List * lp ;
458+ RangeTblEntry * rte = lfirst ( lp ) ;
462459
463- foreach ( lp , parsetree -> rtable )
460+ if ( rte -> rtekind == RTE_SUBQUERY )
464461{
465- RangeTblEntry * rte = lfirst (lp );
462+ ExecCheckXactReadOnly (rte -> subquery );
463+ continue ;
464+ }
466465
467- if (rte -> rtekind != RTE_RELATION )
468- continue ;
466+ if (rte -> rtekind != RTE_RELATION )
467+ continue ;
469468
470- if (! rte -> checkForWrite )
471- continue ;
469+ if (( rte -> requiredPerms & (~ ACL_SELECT )) == 0 )
470+ continue ;
472471
473- if (isTempNamespace (get_rel_namespace (rte -> relid )))
474- continue ;
472+ if (isTempNamespace (get_rel_namespace (rte -> relid )))
473+ continue ;
475474
476- gotofail ;
477- }
475+ gotofail ;
478476}
479477
480478return ;
@@ -511,7 +509,7 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
511509 * rangetable here --- subplan RTEs will be checked during
512510 * ExecInitSubPlan().
513511 */
514- ExecCheckRTPerms (parseTree -> rtable , operation );
512+ ExecCheckRTPerms (parseTree -> rtable );
515513
516514/*
517515 * get information from query descriptor