@ -395,36 +395,43 @@ rewriteRuleAction(Query *parsetree,
* Generate expanded rtable consisting of main parsetree ' s rtable plus
* rule action ' s rtable ; this becomes the complete rtable for the rule
* action . Some of the entries may be unused after we finish rewriting ,
* but we leave them all in place for two reasons :
* but we leave them all in place to avoid having to adjust the query ' s
* varnos . RT entries that are not referenced in the completed jointree
* will be ignored by the planner , so they do not affect query semantics .
*
* We ' d have a much harder job to adjust the query ' s varnos if we
* selectively removed RT entries .
* Also merge RTEPermissionInfo lists to ensure that all permissions ar e
* checked correctly .
*
* If the rule is INSTEAD , then the original query won ' t be executed at
* all , and so its rtable must be preserved so that the executor will do
* the correct permissions checks on it .
* all , and so its rteperminfos must be preserved so that the executor
* will do the correct permissions checks on the relations referenced in
* it . This allows us to check that the caller has , say , insert - permission
* on a view , when the view is not semantically referenced at all in the
* resulting query .
*
* RT entries that are not referenced in the completed jointree will be
* ignored by the planner , so they do not affect query semantics . But any
* permissions checks specified in them will be applied during executor
* startup ( see ExecCheckRTEPerms ( ) ) . This allows us to check that the
* caller has , say , insert - permission on a view , when the view is not
* semantically referenced at all in the resulting query .
* When a rule is not INSTEAD , the permissions checks done using the
* copied entries will be redundant with those done during execution of
* the original query , but we don ' t bother to treat that case differently .
*
* When a rule is not INSTEAD , the permissions checks done on its copied
* RT entries will be redundant with those done during execution of the
* original query , but we don ' t bother to treat that case differently .
*
* NOTE : because planner will destructively alter rtable , we must ensure
* that rule action ' s rtable is separate and shares no substructure with
* the main rtable . Hence do a deep copy here .
*
* Note also that RewriteQuery ( ) relies on the fact that RT entries from
* the original query appear at the start of the expanded rtable , so
* beware of changing this .
* NOTE : because planner will destructively alter rtable and rteperminfos ,
* we must ensure that rule action ' s lists are separate and shares no
* substructure with the main query ' s lists . Hence do a deep copy here
* for both .
*/
sub_action - > rtable = list_concat ( copyObject ( parsetree - > rtable ) ,
sub_action - > rtable ) ;
{
List * rtable_tail = sub_action - > rtable ;
List * perminfos_tail = sub_action - > rteperminfos ;
/*
* RewriteQuery relies on the fact that RT entries from the original
* query appear at the start of the expanded rtable , so we put the
* action ' s original table at the end of the list .
*/
sub_action - > rtable = copyObject ( parsetree - > rtable ) ;
sub_action - > rteperminfos = copyObject ( parsetree - > rteperminfos ) ;
CombineRangeTables ( & sub_action - > rtable , & sub_action - > rteperminfos ,
rtable_tail , perminfos_tail ) ;
}
/*
* There could have been some SubLinks in parsetree ' s rtable , in which
@ -1628,10 +1635,13 @@ rewriteValuesRTEToNulls(Query *parsetree, RangeTblEntry *rte)
/*
* Record in target_rte - > extraUpdatedCols the indexes of any generated columns
* that depend on any columns mentioned in target_rte - > updatedCols .
* columns that depend on any columns mentioned in
* target_perminfo - > updatedCols .
*/
void
fill_extraUpdatedCols ( RangeTblEntry * target_rte , Relation target_relation )
fill_extraUpdatedCols ( RangeTblEntry * target_rte ,
RTEPermissionInfo * target_perminfo ,
Relation target_relation )
{
TupleDesc tupdesc = RelationGetDescr ( target_relation ) ;
TupleConstr * constr = tupdesc - > constr ;
@ -1654,7 +1664,7 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation)
expr = stringToNode ( defval - > adbin ) ;
pull_varattnos ( expr , 1 , & attrs_used ) ;
if ( bms_overlap ( target_rte - > updatedCols , attrs_used ) )
if ( bms_overlap ( target_perminfo - > updatedCols , attrs_used ) )
target_rte - > extraUpdatedCols =
bms_add_member ( target_rte - > extraUpdatedCols ,
defval - > adnum - FirstLowInvalidHeapAttributeNumber ) ;
@ -1747,6 +1757,8 @@ ApplyRetrieveRule(Query *parsetree,
Query * rule_action ;
RangeTblEntry * rte ,
* subrte ;
RTEPermissionInfo * perminfo ,
* sub_perminfo ;
RowMarkClause * rc ;
if ( list_length ( rule - > actions ) ! = 1 )
@ -1787,18 +1799,6 @@ ApplyRetrieveRule(Query *parsetree,
parsetree - > rtable = lappend ( parsetree - > rtable , newrte ) ;
parsetree - > resultRelation = list_length ( parsetree - > rtable ) ;
/*
* There ' s no need to do permissions checks twice , so wipe out the
* permissions info for the original RTE ( we prefer to keep the
* bits set on the result RTE ) .
*/
rte - > requiredPerms = 0 ;
rte - > checkAsUser = InvalidOid ;
rte - > selectedCols = NULL ;
rte - > insertedCols = NULL ;
rte - > updatedCols = NULL ;
rte - > extraUpdatedCols = NULL ;
/*
* For the most part , Vars referencing the view should remain as
* they are , meaning that they implicitly represent OLD values .
@ -1862,12 +1862,6 @@ ApplyRetrieveRule(Query *parsetree,
/*
* Recursively expand any view references inside the view .
*
* Note : this must happen after markQueryForLocking . That way , any UPDATE
* permission bits needed for sub - views are initially applied to their
* RTE_RELATION RTEs by markQueryForLocking , and then transferred to their
* OLD rangetable entries by the action below ( in a recursive call of this
* routine ) .
*/
rule_action = fireRIRrules ( rule_action , activeRIRs ) ;
@ -1876,6 +1870,7 @@ ApplyRetrieveRule(Query *parsetree,
* original RTE to a subquery RTE .
*/
rte = rt_fetch ( rt_index , parsetree - > rtable ) ;
perminfo = getRTEPermissionInfo ( parsetree - > rteperminfos , rte ) ;
rte - > rtekind = RTE_SUBQUERY ;
rte - > subquery = rule_action ;
@ -1885,6 +1880,7 @@ ApplyRetrieveRule(Query *parsetree,
rte - > relkind = 0 ;
rte - > rellockmode = 0 ;
rte - > tablesample = NULL ;
rte - > perminfoindex = 0 ; /* no permission checking for this RTE */
rte - > inh = false ; /* must not be set for a subquery */
/*
@ -1893,19 +1889,12 @@ ApplyRetrieveRule(Query *parsetree,
*/
subrte = rt_fetch ( PRS2_OLD_VARNO , rule_action - > rtable ) ;
Assert ( subrte - > relid = = relation - > rd_id ) ;
subrte - > requiredPerms = rte - > requiredPerms ;
subrte - > checkAsUser = rte - > checkAsUser ;
subrte - > selectedCols = rte - > selectedCols ;
subrte - > insertedCols = rte - > insertedCols ;
subrte - > updatedCols = rte - > updatedCols ;
subrte - > extraUpdatedCols = rte - > extraUpdatedCols ;
rte - > requiredPerms = 0 ; /* no permission check on subquery itself */
rte - > checkAsUser = InvalidOid ;
rte - > selectedCols = NULL ;
rte - > insertedCols = NULL ;
rte - > updatedCols = NULL ;
rte - > extraUpdatedCols = NULL ;
sub_perminfo = getRTEPermissionInfo ( rule_action - > rteperminfos , subrte ) ;
sub_perminfo - > requiredPerms = perminfo - > requiredPerms ;
sub_perminfo - > checkAsUser = perminfo - > checkAsUser ;
sub_perminfo - > selectedCols = perminfo - > selectedCols ;
sub_perminfo - > insertedCols = perminfo - > insertedCols ;
sub_perminfo - > updatedCols = perminfo - > updatedCols ;
return parsetree ;
}
@ -1935,8 +1924,12 @@ markQueryForLocking(Query *qry, Node *jtnode,
if ( rte - > rtekind = = RTE_RELATION )
{
RTEPermissionInfo * perminfo ;
applyLockingClause ( qry , rti , strength , waitPolicy , pushedDown ) ;
rte - > requiredPerms | = ACL_SELECT_FOR_UPDATE ;
perminfo = getRTEPermissionInfo ( qry - > rteperminfos , rte ) ;
perminfo - > requiredPerms | = ACL_SELECT_FOR_UPDATE ;
}
else if ( rte - > rtekind = = RTE_SUBQUERY )
{
@ -3077,6 +3070,9 @@ rewriteTargetView(Query *parsetree, Relation view)
RangeTblEntry * base_rte ;
RangeTblEntry * view_rte ;
RangeTblEntry * new_rte ;
RTEPermissionInfo * base_perminfo ;
RTEPermissionInfo * view_perminfo ;
RTEPermissionInfo * new_perminfo ;
Relation base_rel ;
List * view_targetlist ;
ListCell * lc ;
@ -3213,6 +3209,7 @@ rewriteTargetView(Query *parsetree, Relation view)
base_rt_index = rtr - > rtindex ;
base_rte = rt_fetch ( base_rt_index , viewquery - > rtable ) ;
Assert ( base_rte - > rtekind = = RTE_RELATION ) ;
base_perminfo = getRTEPermissionInfo ( viewquery - > rteperminfos , base_rte ) ;
/*
* Up to now , the base relation hasn ' t been touched at all in our query .
@ -3284,57 +3281,69 @@ rewriteTargetView(Query *parsetree, Relation view)
0 ) ;
/*
* If the view has " security_invoker " set , mark the new target RTE for the
* permissions checks that we want to enforce against the query caller .
* Otherwise we want to enforce them against the view owner .
* If the view has " security_invoker " set , mark the new target relation
* for the permissions checks that we want to enforce against the query
* caller . Otherwise we want to enforce them against the view owner .
*
* At the relation level , require the same INSERT / UPDATE / DELETE
* permissions that the query caller needs against the view . We drop the
* ACL_SELECT bit that is presumably in new_rte - > requiredPerms initially .
* ACL_SELECT bit that is presumably in new_perminfo - > requiredPerms
* initially .
*
* Note : the original view RTE remains in the query ' s rangetable list .
* Although it will be unused in the query plan , we need it there so that
* the executor still performs appropriate permissions checks for the
* query caller ' s use of the view .
* Note : the original view ' s RTEPermissionInfo remains in the query ' s
* rteperminfos so that the executor still performs appropriate
* permissions checks for the query caller ' s use of the view .
*/
view_perminfo = getRTEPermissionInfo ( parsetree - > rteperminfos , view_rte ) ;
/*
* Disregard the perminfo in viewquery - > rteperminfos that the base_rte
* would currently be pointing at , because we ' d like it to point now to a
* new one that will be filled below . Must set perminfoindex to 0 to not
* trip over the Assert in addRTEPermissionInfo ( ) .
*/
new_rte - > perminfoindex = 0 ;
new_perminfo = addRTEPermissionInfo ( & parsetree - > rteperminfos , new_rte ) ;
if ( RelationHasSecurityInvoker ( view ) )
new_rte - > checkAsUser = InvalidOid ;
new_perminfo - > checkAsUser = InvalidOid ;
else
new_rte - > checkAsUser = view - > rd_rel - > relowner ;
new_rte - > requiredPerms = view_rte - > requiredPerms ;
new_perminfo - > checkAsUser = view - > rd_rel - > relowner ;
new_perminfo - > requiredPerms = view_perminfo - > requiredPerms ;
/*
* Now for the per - column permissions bits .
*
* Initially , new_rte contains selectedCols permission check bits for all
* base - rel columns referenced by the view , but since the view is a SELECT
* query its insertedCols / updatedCols is empty . We set insertedCols and
* updatedCols to include all the columns the outer query is trying to
* modify , adjusting the column numbers as needed . But we leave
* selectedCols as - is , so the view owner must have read permission for all
* columns used in the view definition , even if some of them are not read
* by the outer query . We could try to limit selectedCols to only columns
* used in the transformed query , but that does not correspond to what
* happens in ordinary SELECT usage of a view : all referenced columns must
* have read permission , even if optimization finds that some of them can
* be discarded during query transformation . The flattening we ' re doing
* here is an optional optimization , too . ( If you are unpersuaded and
* want to change this , note that applying adjust_view_column_set to
* view_rte - > selectedCols is clearly * not * the right answer , since that
* neglects base - rel columns used in the view ' s WHERE quals . )
* Initially , new_perminfo ( base_perminfo ) contains selectedCols
* permission check bits for all base - rel columns referenced by the view ,
* but since the view is a SELECT query its insertedCols / updatedCols is
* empty . We set insertedCols and updatedCols to include all the columns
* the outer query is trying to modify , adjusting the column numbers as
* needed . But we leave selectedCols as - is , so the view owner must have
* read permission for all columns used in the view definition , even if
* some of them are not read by the outer query . We could try to limit
* selectedCols to only columns used in the transformed query , but that
* does not correspond to what happens in ordinary SELECT usage of a view :
* all referenced columns must have read permission , even if optimization
* finds that some of them can be discarded during query transformation .
* The flattening we ' re doing here is an optional optimization , too . ( If
* you are unpersuaded and want to change this , note that applying
* adjust_view_column_set to view_perminfo - > selectedCols is clearly * not *
* the right answer , since that neglects base - rel columns used in the
* view ' s WHERE quals . )
*
* This step needs the modified view targetlist , so we have to do things
* in this order .
*/
Assert ( bms_is_empty ( new_rte - > insertedCols ) & &
bms_is_empty ( new_rte - > updatedCols ) ) ;
Assert ( bms_is_empty ( new_perminfo - > insertedCols ) & &
bms_is_empty ( new_perminfo - > updatedCols ) ) ;
new_perminfo - > selectedCols = base_perminfo - > selectedCols ;
new_rte - > insertedCols = adjust_view_column_set ( view_rte - > insertedCols ,
view_targetlist ) ;
new_perminfo - > insertedCols =
adjust_view_column_set ( view_perminfo - > insertedCols , view_targetlist ) ;
new_rte - > updatedCols = adjust_view_column_set ( view_rte - > updatedCols ,
view_targetlist ) ;
new_perminfo - > updatedCols =
adjust_view_column_set ( view_perminfo - > updatedCols , view_targetlist ) ;
/*
* Move any security barrier quals from the view RTE onto the new target
@ -3438,7 +3447,7 @@ rewriteTargetView(Query *parsetree, Relation view)
* from the view , hence we need a new column alias list ) . This should
* match transformOnConflictClause . In particular , note that the
* relkind is set to composite to signal that we ' re not dealing with
* an actual relation , and no permissions checks are wanted .
* an actual relation .
*/
old_exclRelIndex = parsetree - > onConflict - > exclRelIndex ;
@ -3449,8 +3458,8 @@ rewriteTargetView(Query *parsetree, Relation view)
false , false ) ;
new_exclRte = new_exclNSItem - > p_rte ;
new_exclRte - > relkind = RELKIND_COMPOSITE_TYPE ;
new_exclRte - > requiredPerms = 0 ;
/* other permissions fields in new_exclRte are already empty */
/* Ignore the RTEPermissionInfo that would've been added. */
new_exclRte - > perminfoindex = 0 ;
parsetree - > rtable = lappend ( parsetree - > rtable , new_exclRte ) ;
new_exclRelIndex = parsetree - > onConflict - > exclRelIndex =
@ -3728,6 +3737,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
{
int result_relation ;
RangeTblEntry * rt_entry ;
RTEPermissionInfo * rt_perminfo ;
Relation rt_entry_relation ;
List * locks ;
int product_orig_rt_length ;
@ -3740,6 +3750,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
Assert ( result_relation ! = 0 ) ;
rt_entry = rt_fetch ( result_relation , parsetree - > rtable ) ;
Assert ( rt_entry - > rtekind = = RTE_RELATION ) ;
rt_perminfo = getRTEPermissionInfo ( parsetree - > rteperminfos , rt_entry ) ;
/*
* We can use NoLock here since either the parser or
@ -3833,7 +3844,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
NULL , 0 , NULL ) ;
/* Also populate extraUpdatedCols (for generated columns) */
fill_extraUpdatedCols ( rt_entry , rt_entry_relation ) ;
fill_extraUpdatedCols ( rt_entry , rt_perminfo , rt_ entry_relation ) ;
}
else if ( event = = CMD_MERGE )
{