2727 *
2828 *
2929 * IDENTIFICATION
30- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.109 2000/02/15 03:36:49 thomas Exp $
30+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.110 2000/03/09 05:15:33 tgl Exp $
3131 *
3232 *-------------------------------------------------------------------------
3333 */
4646#include "utils/builtins.h"
4747#include "utils/syscache.h"
4848
49- void ExecCheckPerms ( CmdType operation , int resultRelation , List * rangeTable ,
50- Query * parseTree );
51-
49+ /* XXX no points for style */
50+ extern TupleTableSlot * EvalPlanQual ( EState * estate , Index rti ,
51+ ItemPointer tid );
5252
5353/* decls for local routines only used within this module */
5454static TupleDesc InitPlan (CmdType operation ,
@@ -72,13 +72,18 @@ static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
7272EState * estate );
7373static void ExecReplace (TupleTableSlot * slot ,ItemPointer tupleid ,
7474EState * estate );
75-
76- TupleTableSlot * EvalPlanQual (EState * estate ,Index rti ,ItemPointer tid );
7775static TupleTableSlot * EvalPlanQualNext (EState * estate );
78-
79-
76+ static void ExecCheckQueryPerms (CmdType operation ,Query * parseTree ,
77+ Plan * plan );
78+ static void ExecCheckPlanPerms (Plan * plan ,CmdType operation ,
79+ int resultRelation ,bool resultIsScanned );
80+ static void ExecCheckRTPerms (List * rangeTable ,CmdType operation ,
81+ int resultRelation ,bool resultIsScanned );
82+ static void ExecCheckRTEPerms (RangeTblEntry * rte ,CmdType operation ,
83+ bool isResultRelation ,bool resultIsScanned );
8084/* end of local decls */
8185
86+
8287/* ----------------------------------------------------------------
8388 *ExecutorStart
8489 *
@@ -378,104 +383,257 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
378383}
379384}
380385
381- void
382- ExecCheckPerms (CmdType operation ,
383- int resultRelation ,
384- List * rangeTable ,
385- Query * parseTree )
386+
387+ /*
388+ * ExecCheckQueryPerms
389+ *Check access permissions for all relations referenced in a query.
390+ */
391+ static void
392+ ExecCheckQueryPerms (CmdType operation ,Query * parseTree ,Plan * plan )
386393{
387- int rtindex = 0 ;
394+ List * rangeTable = parseTree -> rtable ;
395+ int resultRelation = parseTree -> resultRelation ;
396+ bool resultIsScanned = false;
388397List * lp ;
389- List * qvars ,
390- * tvars ;
391- int32 ok = 1 ,
392- aclcheck_result = -1 ;
393- char * opstr ;
394- char * relName = NULL ;
395- char * userName ;
396398
397- #define CHECK (MODE )pg_aclcheck(relName, userName, MODE)
399+ /*
400+ * If we have a result relation, determine whether the result rel is
401+ * scanned or merely written. If scanned, we will insist on read
402+ * permission as well as modify permission.
403+ */
404+ if (resultRelation > 0 )
405+ {
406+ List * qvars = pull_varnos (parseTree -> qual );
407+ List * tvars = pull_varnos ((Node * )parseTree -> targetList );
398408
399- userName = GetPgUserName ();
409+ resultIsScanned = (intMember (resultRelation ,qvars )||
410+ intMember (resultRelation ,tvars ));
411+ freeList (qvars );
412+ freeList (tvars );
413+ }
400414
401- foreach (lp ,rangeTable )
415+ /*
416+ * Check RTEs in the query's primary rangetable.
417+ */
418+ ExecCheckRTPerms (rangeTable ,operation ,resultRelation ,resultIsScanned );
419+
420+ /*
421+ * Check SELECT FOR UPDATE access rights.
422+ */
423+ foreach (lp ,parseTree -> rowMark )
402424{
403- RangeTblEntry * rte = lfirst (lp );
425+ RowMark * rm = lfirst (lp );
404426
405- ++ rtindex ;
427+ if (!(rm -> info & ROW_ACL_FOR_UPDATE ))
428+ continue ;
429+
430+ ExecCheckRTEPerms (rt_fetch (rm -> rti ,rangeTable ),
431+ CMD_UPDATE , true, false);
432+ }
433+
434+ /*
435+ * Search for subplans and APPEND nodes to check their rangetables.
436+ */
437+ ExecCheckPlanPerms (plan ,operation ,resultRelation ,resultIsScanned );
438+ }
439+
440+ /*
441+ * ExecCheckPlanPerms
442+ *Recursively scan the plan tree to check access permissions in
443+ *subplans.
444+ *
445+ * We also need to look at the local rangetables in Append plan nodes,
446+ * which is pretty bogus --- most likely, those tables should be mentioned
447+ * in the query's main rangetable. But at the moment, they're not.
448+ */
449+ static void
450+ ExecCheckPlanPerms (Plan * plan ,CmdType operation ,
451+ int resultRelation ,bool resultIsScanned )
452+ {
453+ List * subp ;
454+
455+ if (plan == NULL )
456+ return ;
457+
458+ /* Check subplans, which we assume are plain SELECT queries */
459+
460+ foreach (subp ,plan -> initPlan )
461+ {
462+ SubPlan * subplan = (SubPlan * )lfirst (subp );
463+
464+ ExecCheckRTPerms (subplan -> rtable ,CMD_SELECT ,0 , false);
465+ ExecCheckPlanPerms (subplan -> plan ,CMD_SELECT ,0 , false);
466+ }
467+ foreach (subp ,plan -> subPlan )
468+ {
469+ SubPlan * subplan = (SubPlan * )lfirst (subp );
470+
471+ ExecCheckRTPerms (subplan -> rtable ,CMD_SELECT ,0 , false);
472+ ExecCheckPlanPerms (subplan -> plan ,CMD_SELECT ,0 , false);
473+ }
474+
475+ /* Check lower plan nodes */
476+
477+ ExecCheckPlanPerms (plan -> lefttree ,operation ,
478+ resultRelation ,resultIsScanned );
479+ ExecCheckPlanPerms (plan -> righttree ,operation ,
480+ resultRelation ,resultIsScanned );
481+
482+ /* Do node-type-specific checks */
406483
407- if (rte -> skipAcl )
484+ switch (nodeTag (plan ))
485+ {
486+ case T_Append :
408487{
488+ Append * app = (Append * )plan ;
489+ List * appendplans ;
409490
410- /*
411- * This happens if the access to this table is due to a view
412- * query rewriting - the rewrite handler checked the
413- *permissions against the view owner, so we just skip this
414- *entry .
415- */
416- continue ;
417- }
491+ if ( app -> inheritrelid > 0 )
492+ {
493+ /*
494+ *Append implements expansion of inheritance; all members
495+ *of inheritrtable list will be plugged into same RTE slot .
496+ * Therefore, they are either all result relations or none.
497+ */
498+ List * rtable ;
418499
419- relName = rte -> relname ;
420- if (rtindex == resultRelation )
421- {/* this is the result relation */
422- qvars = pull_varnos (parseTree -> qual );
423- tvars = pull_varnos ((Node * )parseTree -> targetList );
424- if (intMember (resultRelation ,qvars )||
425- intMember (resultRelation ,tvars ))
500+ foreach (rtable ,app -> inheritrtable )
501+ {
502+ ExecCheckRTEPerms ((RangeTblEntry * )lfirst (rtable ),
503+ operation ,
504+ (app -> inheritrelid == resultRelation ),
505+ resultIsScanned );
506+ }
507+ }
508+ else
426509{
427- /* result relation is scanned */
428- ok = ((aclcheck_result = CHECK (ACL_RD ))== ACLCHECK_OK );
429- opstr = "read" ;
430- if (!ok )
431- break ;
510+ /* Append implements UNION, which must be a SELECT */
511+ List * rtables ;
512+
513+ foreach (rtables ,app -> unionrtables )
514+ {
515+ ExecCheckRTPerms ((List * )lfirst (rtables ),
516+ CMD_SELECT ,0 , false);
517+ }
432518}
433- switch (operation )
519+
520+ /* Check appended plans */
521+ foreach (appendplans ,app -> appendplans )
434522{
435- case CMD_INSERT :
436- ok = ((aclcheck_result = CHECK (ACL_AP ))== ACLCHECK_OK )||
437- ((aclcheck_result = CHECK (ACL_WR ))== ACLCHECK_OK );
438- opstr = "append" ;
439- break ;
440- case CMD_DELETE :
441- case CMD_UPDATE :
442- ok = ((aclcheck_result = CHECK (ACL_WR ))== ACLCHECK_OK );
443- opstr = "write" ;
444- break ;
445- default :
446- elog (ERROR ,"ExecCheckPerms: bogus operation %d" ,
447- operation );
523+ ExecCheckPlanPerms ((Plan * )lfirst (appendplans ),
524+ operation ,
525+ resultRelation ,
526+ resultIsScanned );
448527}
528+ break ;
449529}
450- else
451- {
452- ok = ((aclcheck_result = CHECK (ACL_RD ))== ACLCHECK_OK );
453- opstr = "read" ;
454- }
455- if (!ok )
530+
531+ default :
456532break ;
457533}
458- if (!ok )
459- elog (ERROR ,"%s: %s" ,relName ,aclcheck_error_strings [aclcheck_result ]);
534+ }
460535
461- if (parseTree != NULL && parseTree -> rowMark != NULL )
536+ /*
537+ * ExecCheckRTPerms
538+ *Check access permissions for all relations listed in a range table.
539+ *
540+ * If resultRelation is not 0, it is the RT index of the relation to be
541+ * treated as the result relation. All other relations are assumed to be
542+ * read-only for the query.
543+ */
544+ static void
545+ ExecCheckRTPerms (List * rangeTable ,CmdType operation ,
546+ int resultRelation ,bool resultIsScanned )
547+ {
548+ int rtindex = 0 ;
549+ List * lp ;
550+
551+ foreach (lp ,rangeTable )
462552{
463- foreach (lp ,parseTree -> rowMark )
464- {
465- RowMark * rm = lfirst (lp );
553+ RangeTblEntry * rte = lfirst (lp );
466554
467- if (!(rm -> info & ROW_ACL_FOR_UPDATE ))
468- continue ;
555+ ++ rtindex ;
556+
557+ ExecCheckRTEPerms (rte ,
558+ operation ,
559+ (rtindex == resultRelation ),
560+ resultIsScanned );
561+ }
562+ }
563+
564+ /*
565+ * ExecCheckRTEPerms
566+ *Check access permissions for a single RTE.
567+ */
568+ static void
569+ ExecCheckRTEPerms (RangeTblEntry * rte ,CmdType operation ,
570+ bool isResultRelation ,bool resultIsScanned )
571+ {
572+ char * relName ;
573+ char * userName ;
574+ int32 aclcheck_result ;
575+
576+ if (rte -> skipAcl )
577+ {
578+ /*
579+ * This happens if the access to this table is due to a view
580+ * query rewriting - the rewrite handler already checked the
581+ * permissions against the view owner, so we just skip this entry.
582+ */
583+ return ;
584+ }
469585
470- relName = rt_fetch (rm -> rti ,rangeTable )-> relname ;
471- ok = ((aclcheck_result = CHECK (ACL_WR ))== ACLCHECK_OK );
472- opstr = "write" ;
473- if (!ok )
474- elog (ERROR ,"%s: %s" ,relName ,aclcheck_error_strings [aclcheck_result ]);
586+ relName = rte -> relname ;
587+
588+ /*
589+ * Note: GetPgUserName is presently fast enough that there's no harm
590+ * in calling it separately for each RTE. If that stops being true,
591+ * we could call it once in ExecCheckQueryPerms and pass the userName
592+ * down from there. But for now, no need for the extra clutter.
593+ */
594+ userName = GetPgUserName ();
595+
596+ #define CHECK (MODE )pg_aclcheck(relName, userName, MODE)
597+
598+ if (isResultRelation )
599+ {
600+ if (resultIsScanned )
601+ {
602+ aclcheck_result = CHECK (ACL_RD );
603+ if (aclcheck_result != ACLCHECK_OK )
604+ elog (ERROR ,"%s: %s" ,
605+ relName ,aclcheck_error_strings [aclcheck_result ]);
606+ }
607+ switch (operation )
608+ {
609+ case CMD_INSERT :
610+ /* Accept either APPEND or WRITE access for this */
611+ aclcheck_result = CHECK (ACL_AP );
612+ if (aclcheck_result != ACLCHECK_OK )
613+ aclcheck_result = CHECK (ACL_WR );
614+ break ;
615+ case CMD_DELETE :
616+ case CMD_UPDATE :
617+ aclcheck_result = CHECK (ACL_WR );
618+ break ;
619+ default :
620+ elog (ERROR ,"ExecCheckRTEPerms: bogus operation %d" ,
621+ operation );
622+ aclcheck_result = ACLCHECK_OK ;/* keep compiler quiet */
623+ break ;
475624}
476625}
626+ else
627+ {
628+ aclcheck_result = CHECK (ACL_RD );
629+ }
630+
631+ if (aclcheck_result != ACLCHECK_OK )
632+ elog (ERROR ,"%s: %s" ,
633+ relName ,aclcheck_error_strings [aclcheck_result ]);
477634}
478635
636+
479637/* ===============================================================
480638 * ===============================================================
481639 static routines follow
@@ -514,16 +672,19 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
514672TupleDesc tupType ;
515673List * targetList ;
516674
675+ /*
676+ * Do permissions checks.
677+ */
678+ #ifndef NO_SECURITY
679+ ExecCheckQueryPerms (operation ,parseTree ,plan );
680+ #endif
681+
517682/*
518683 * get information from query descriptor
519684 */
520685rangeTable = parseTree -> rtable ;
521686resultRelation = parseTree -> resultRelation ;
522687
523- #ifndef NO_SECURITY
524- ExecCheckPerms (operation ,resultRelation ,rangeTable ,parseTree );
525- #endif
526-
527688/*
528689 * initialize the node's execution state
529690 */