@ -73,8 +73,8 @@ static TargetEntry *process_matched_tle(TargetEntry *src_tle,
TargetEntry * prior_tle ,
TargetEntry * prior_tle ,
const char * attrName ) ;
const char * attrName ) ;
static Node * get_assignment_input ( Node * node ) ;
static Node * get_assignment_input ( Node * node ) ;
static void rewriteValuesRTE ( RangeTblEnt ry * rte , Relation ta rge t_r elation ,
static bool rewriteValuesRTE ( Que ry * pa rse tre e, Rang eTb lEntry * rte ,
List * attrnos ) ;
Relation target_relation , List * attrnos , bool force_null s ) ;
static void markQueryForLocking ( Query * qry , Node * jtnode ,
static void markQueryForLocking ( Query * qry , Node * jtnode ,
LockClauseStrength strength , LockWaitPolicy waitPolicy ,
LockClauseStrength strength , LockWaitPolicy waitPolicy ,
bool pushedDown ) ;
bool pushedDown ) ;
@ -1219,29 +1219,102 @@ searchForDefault(RangeTblEntry *rte)
* the appropriate default expressions . The other aspects of targetlist
* the appropriate default expressions . The other aspects of targetlist
* rewriting need be applied only to the query ' s targetlist proper .
* rewriting need be applied only to the query ' s targetlist proper .
*
*
* For an auto - updatable view , each DEFAULT item in the VALUES list is
* replaced with the default from the view , if it has one . Otherwise it is
* left untouched so that the underlying base relation ' s default can be
* applied instead ( when we later recurse to here after rewriting the query
* to refer to the base relation instead of the view ) .
*
* For other types of relation , including rule - and trigger - updatable views ,
* all DEFAULT items are replaced , and if the target relation doesn ' t have a
* default , the value is explicitly set to NULL .
*
* Additionally , if force_nulls is true , the target relation ' s defaults are
* ignored and all DEFAULT items in the VALUES list are explicitly set to
* NULL , regardless of the target relation ' s type . This is used for the
* product queries generated by DO ALSO rules attached to an auto - updatable
* view , for which we will have already called this function with force_nulls
* false . For these product queries , we must then force any remaining DEFAULT
* items to NULL to provide concrete values for the rule actions .
* Essentially , this is a mix of the 2 cases above - - - the original query is
* an insert into an auto - updatable view , and the product queries are inserts
* into a rule - updatable view .
*
* Note that we currently can ' t support subscripted or field assignment
* Note that we currently can ' t support subscripted or field assignment
* in the multi - VALUES case . The targetlist will contain simple Vars
* in the multi - VALUES case . The targetlist will contain simple Vars
* referencing the VALUES RTE , and therefore process_matched_tle ( ) will
* referencing the VALUES RTE , and therefore process_matched_tle ( ) will
* reject any such attempt with " multiple assignments to same column " .
* reject any such attempt with " multiple assignments to same column " .
*
* Returns true if all DEFAULT items were replaced , and false if some were
* left untouched .
*/
*/
static void
static bool
rewriteValuesRTE ( RangeTblEntry * rte , Relation target_relation , List * attrnos )
rewriteValuesRTE ( Query * parsetree , RangeTblEntry * rte ,
Relation target_relation , List * attrnos , bool force_nulls )
{
{
List * newValues ;
List * newValues ;
ListCell * lc ;
ListCell * lc ;
bool isAutoUpdatableView ;
bool allReplaced ;
/*
/*
* Rebuilding all the lists is a pretty expensive proposition in a big
* Rebuilding all the lists is a pretty expensive proposition in a big
* VALUES list , and it ' s a waste of time if there aren ' t any DEFAULT
* VALUES list , and it ' s a waste of time if there aren ' t any DEFAULT
* placeholders . So first scan to see if there are any .
* placeholders . So first scan to see if there are any .
*
* We skip this check if force_nulls is true , because we know that there
* are DEFAULT items present in that case .
*/
*/
if ( ! searchForDefault ( rte ) )
if ( ! force_nulls & & ! searchForDefault ( rte ) )
return ; /* nothing to do */
return true ; /* nothing to do */
/* Check list lengths (we can assume all the VALUES sublists are alike) */
/* Check list lengths (we can assume all the VALUES sublists are alike) */
Assert ( list_length ( attrnos ) = = list_length ( linitial ( rte - > values_lists ) ) ) ;
Assert ( list_length ( attrnos ) = = list_length ( linitial ( rte - > values_lists ) ) ) ;
/*
* Check if the target relation is an auto - updatable view , in which case
* unresolved defaults will be left untouched rather than being set to
* NULL . If force_nulls is true , we always set DEFAULT items to NULL , so
* skip this check in that case - - - it isn ' t an auto - updatable view .
*/
isAutoUpdatableView = false ;
if ( ! force_nulls & &
target_relation - > rd_rel - > relkind = = RELKIND_VIEW & &
! view_has_instead_trigger ( target_relation , CMD_INSERT ) )
{
List * locks ;
bool hasUpdate ;
bool found ;
ListCell * l ;
/* Look for an unconditional DO INSTEAD rule */
locks = matchLocks ( CMD_INSERT , target_relation - > rd_rules ,
parsetree - > resultRelation , parsetree , & hasUpdate ) ;
found = false ;
foreach ( l , locks )
{
RewriteRule * rule_lock = ( RewriteRule * ) lfirst ( l ) ;
if ( rule_lock - > isInstead & &
rule_lock - > qual = = NULL )
{
found = true ;
break ;
}
}
/*
* If we didn ' t find an unconditional DO INSTEAD rule , assume that the
* view is auto - updatable . If it isn ' t , rewriteTargetView ( ) will
* throw an error .
*/
if ( ! found )
isAutoUpdatableView = true ;
}
newValues = NIL ;
newValues = NIL ;
allReplaced = true ;
foreach ( lc , rte - > values_lists )
foreach ( lc , rte - > values_lists )
{
{
List * sublist = ( List * ) lfirst ( lc ) ;
List * sublist = ( List * ) lfirst ( lc ) ;
@ -1261,17 +1334,26 @@ rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos)
att_tup = TupleDescAttr ( target_relation - > rd_att , attrno - 1 ) ;
att_tup = TupleDescAttr ( target_relation - > rd_att , attrno - 1 ) ;
if ( ! att_tup - > attisdropped )
if ( ! force_nulls & & ! att_tup - > attisdropped )
new_expr = build_column_default ( target_relation , attrno ) ;
new_expr = build_column_default ( target_relation , attrno ) ;
else
else
new_expr = NULL ; /* force a NULL if dropped */
new_expr = NULL ; /* force a NULL if dropped */
/*
/*
* If there is no default ( ie , default is effectively NULL ) ,
* If there is no default ( ie , default is effectively NULL ) ,
* we ' ve got to explicitly set the column to NULL .
* we ' ve got to explicitly set the column to NULL , unless the
* target relation is an auto - updatable view .
*/
*/
if ( ! new_expr )
if ( ! new_expr )
{
{
if ( isAutoUpdatableView )
{
/* Leave the value untouched */
newList = lappend ( newList , col ) ;
allReplaced = false ;
continue ;
}
new_expr = ( Node * ) makeConst ( att_tup - > atttypid ,
new_expr = ( Node * ) makeConst ( att_tup - > atttypid ,
- 1 ,
- 1 ,
att_tup - > attcollation ,
att_tup - > attcollation ,
@ -1296,6 +1378,8 @@ rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos)
newValues = lappend ( newValues , newList ) ;
newValues = lappend ( newValues , newList ) ;
}
}
rte - > values_lists = newValues ;
rte - > values_lists = newValues ;
return allReplaced ;
}
}
@ -3383,6 +3467,9 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
List * locks ;
List * locks ;
List * product_queries ;
List * product_queries ;
bool hasUpdate = false ;
bool hasUpdate = false ;
List * attrnos = NIL ;
int values_rte_index = 0 ;
bool defaults_remaining = false ;
result_relation = parsetree - > resultRelation ;
result_relation = parsetree - > resultRelation ;
Assert ( result_relation ! = 0 ) ;
Assert ( result_relation ! = 0 ) ;
@ -3416,14 +3503,15 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
parsetree - > rtable ) ;
parsetree - > rtable ) ;
if ( rte - > rtekind = = RTE_VALUES )
if ( rte - > rtekind = = RTE_VALUES )
{
values_rte = rte ;
values_rte = rte ;
values_rte_index = rtr - > rtindex ;
}
}
}
}
}
if ( values_rte )
if ( values_rte )
{
{
List * attrnos ;
/* Process the main targetlist ... */
/* Process the main targetlist ... */
parsetree - > targetList = rewriteTargetListIU ( parsetree - > targetList ,
parsetree - > targetList = rewriteTargetListIU ( parsetree - > targetList ,
parsetree - > commandType ,
parsetree - > commandType ,
@ -3432,7 +3520,9 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
parsetree - > resultRelation ,
parsetree - > resultRelation ,
& attrnos ) ;
& attrnos ) ;
/* ... and the VALUES expression lists */
/* ... and the VALUES expression lists */
rewriteValuesRTE ( values_rte , rt_entry_relation , attrnos ) ;
if ( ! rewriteValuesRTE ( parsetree , values_rte ,
rt_entry_relation , attrnos , false ) )
defaults_remaining = true ;
}
}
else
else
{
{
@ -3487,6 +3577,33 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
& returning ,
& returning ,
& qual_product ) ;
& qual_product ) ;
/*
* If we have a VALUES RTE with any remaining untouched DEFAULT items ,
* and we got any product queries , finalize the VALUES RTE for each
* product query ( replacing the remaining DEFAULT items with NULLs ) .
* We don ' t do this for the original query , because we know that it
* must be an auto - insert on a view , and so should use the base
* relation ' s defaults for any remaining DEFAULT items .
*/
if ( defaults_remaining & & product_queries ! = NIL )
{
ListCell * n ;
/*
* Each product query has its own copy of the VALUES RTE at the
* same index in the rangetable , so we must finalize each one .
*/
foreach ( n , product_queries )
{
Query * pt = ( Query * ) lfirst ( n ) ;
RangeTblEntry * values_rte = rt_fetch ( values_rte_index ,
pt - > rtable ) ;
rewriteValuesRTE ( pt , values_rte , rt_entry_relation , attrnos ,
true ) ; /* Force remaining defaults to NULL */
}
}
/*
/*
* If there were no INSTEAD rules , and the target relation is a view
* If there were no INSTEAD rules , and the target relation is a view
* without any INSTEAD OF triggers , see if the view can be
* without any INSTEAD OF triggers , see if the view can be