@ -92,6 +92,9 @@ static bool ExecCheckPermissionsModified(Oid relOid, Oid userid,
AclMode requiredPerms ) ;
AclMode requiredPerms ) ;
static void ExecCheckXactReadOnly ( PlannedStmt * plannedstmt ) ;
static void ExecCheckXactReadOnly ( PlannedStmt * plannedstmt ) ;
static void EvalPlanQualStart ( EPQState * epqstate , Plan * planTree ) ;
static void EvalPlanQualStart ( EPQState * epqstate , Plan * planTree ) ;
static void ReportNotNullViolationError ( ResultRelInfo * resultRelInfo ,
TupleTableSlot * slot ,
EState * estate , int attnum ) ;
/* end of local decls */
/* end of local decls */
@ -1372,6 +1375,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo - > ri_FdwState = NULL ;
resultRelInfo - > ri_FdwState = NULL ;
resultRelInfo - > ri_usesFdwDirectModify = false ;
resultRelInfo - > ri_usesFdwDirectModify = false ;
resultRelInfo - > ri_CheckConstraintExprs = NULL ;
resultRelInfo - > ri_CheckConstraintExprs = NULL ;
resultRelInfo - > ri_GenVirtualNotNullConstraintExprs = NULL ;
resultRelInfo - > ri_GeneratedExprsI = NULL ;
resultRelInfo - > ri_GeneratedExprsI = NULL ;
resultRelInfo - > ri_GeneratedExprsU = NULL ;
resultRelInfo - > ri_GeneratedExprsU = NULL ;
resultRelInfo - > ri_projectReturning = NULL ;
resultRelInfo - > ri_projectReturning = NULL ;
@ -1842,7 +1846,7 @@ ExecutePlan(QueryDesc *queryDesc,
/*
/*
* ExecRelCheck - - - check that tuple meets constraints for result relation
* ExecRelCheck - - - check that tuple meets check c onstraints for result relation
*
*
* Returns NULL if OK , else name of failed check constraint
* Returns NULL if OK , else name of failed check constraint
*/
*/
@ -2056,11 +2060,15 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
TupleDesc tupdesc = RelationGetDescr ( rel ) ;
TupleDesc tupdesc = RelationGetDescr ( rel ) ;
TupleConstr * constr = tupdesc - > constr ;
TupleConstr * constr = tupdesc - > constr ;
Bitmapset * modifiedCols ;
Bitmapset * modifiedCols ;
List * notnull_virtual_attrs = NIL ;
Assert ( constr ) ; /* we should not be called otherwise */
Assert ( constr ) ; /* we should not be called otherwise */
/*
/*
* Verify not - null constraints .
* Verify not - null constraints .
*
* Not - null constraints on virtual generated columns are collected and
* checked separately below .
*/
*/
if ( constr - > has_not_null )
if ( constr - > has_not_null )
{
{
@ -2068,59 +2076,24 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
{
{
Form_pg_attribute att = TupleDescAttr ( tupdesc , attnum - 1 ) ;
Form_pg_attribute att = TupleDescAttr ( tupdesc , attnum - 1 ) ;
if ( att - > attnotnull & & slot_attisnull ( slot , attnum ) )
if ( att - > attnotnull & & att - > attgenerated = = ATTRIBUTE_GENERATED_VIRTUAL )
{
notnull_virtual_attrs = lappend_int ( notnull_virtual_attrs , attnum ) ;
char * val_desc ;
else if ( att - > attnotnull & & slot_attisnull ( slot , attnum ) )
Relation orig_rel = rel ;
ReportNotNullViolationError ( resultRelInfo , slot , estate , attnum ) ;
TupleDesc orig_tupdesc = RelationGetDescr ( rel ) ;
}
}
/*
* If the tuple has been routed , it ' s been converted to the
* partition ' s rowtype , which might differ from the root
* table ' s . We must convert it back to the root table ' s
* rowtype so that val_desc shown error message matches the
* input tuple .
*/
if ( resultRelInfo - > ri_RootResultRelInfo )
{
ResultRelInfo * rootrel = resultRelInfo - > ri_RootResultRelInfo ;
AttrMap * map ;
tupdesc = RelationGetDescr ( rootrel - > ri_RelationDesc ) ;
/* a reverse map */
map = build_attrmap_by_name_if_req ( orig_tupdesc ,
tupdesc ,
false ) ;
/*
/*
* Partition - specific slot ' s tupdesc can ' t be changed , so
* Verify not - null constraints on virtual generated column , if any .
* allocate a new one .
*/
*/
if ( notnull_virtual_attrs )
if ( map ! = NULL )
{
slot = execute_attr_map_slot ( map , slot ,
AttrNumber attnum ;
MakeTupleTableSlot ( tupdesc , & TTSOpsVirtual ) ) ;
modifiedCols = bms_union ( ExecGetInsertedCols ( rootrel , estate ) ,
ExecGetUpdatedCols ( rootrel , estate ) ) ;
rel = rootrel - > ri_RelationDesc ;
}
else
modifiedCols = bms_union ( ExecGetInsertedCols ( resultRelInfo , estate ) ,
ExecGetUpdatedCols ( resultRelInfo , estate ) ) ;
val_desc = ExecBuildSlotValueDescription ( RelationGetRelid ( rel ) ,
slot ,
tupdesc ,
modifiedCols ,
64 ) ;
ereport ( ERROR ,
attnum = ExecRelGenVirtualNotNull ( resultRelInfo , slot , estate ,
errcode ( ERRCODE_NOT_NULL_VIOLATION ) ,
notnull_virtual_attrs ) ;
errmsg ( " null value in column \" %s \" of relation \" %s \" violates not-null constraint " ,
if ( attnum ! = InvalidAttrNumber )
NameStr ( att - > attname ) ,
ReportNotNullViolationError ( resultRelInfo , slot , estate , attnum ) ;
RelationGetRelationName ( orig_rel ) ) ,
val_desc ? errdetail ( " Failing row contains %s. " , val_desc ) : 0 ,
errtablecol ( orig_rel , attnum ) ) ;
}
}
}
}
/*
/*
@ -2135,7 +2108,12 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
char * val_desc ;
char * val_desc ;
Relation orig_rel = rel ;
Relation orig_rel = rel ;
/* See the comment above. */
/*
* If the tuple has been routed , it ' s been converted to the
* partition ' s rowtype , which might differ from the root table ' s .
* We must convert it back to the root table ' s rowtype so that
* val_desc shown error message matches the input tuple .
*/
if ( resultRelInfo - > ri_RootResultRelInfo )
if ( resultRelInfo - > ri_RootResultRelInfo )
{
{
ResultRelInfo * rootrel = resultRelInfo - > ri_RootResultRelInfo ;
ResultRelInfo * rootrel = resultRelInfo - > ri_RootResultRelInfo ;
@ -2177,6 +2155,142 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
}
}
}
}
/*
* Verify not - null constraints on virtual generated columns of the given
* tuple slot .
*
* Return value of InvalidAttrNumber means all not - null constraints on virtual
* generated columns are satisfied . A return value > 0 means a not - null
* violation happened for that attribute .
*
* notnull_virtual_attrs is the list of the attnums of virtual generated column with
* not - null constraints .
*/
AttrNumber
ExecRelGenVirtualNotNull ( ResultRelInfo * resultRelInfo , TupleTableSlot * slot ,
EState * estate , List * notnull_virtual_attrs )
{
Relation rel = resultRelInfo - > ri_RelationDesc ;
ExprContext * econtext ;
MemoryContext oldContext ;
/*
* We implement this by building a NullTest node for each virtual
* generated column , which we cache in resultRelInfo , and running those
* through ExecCheck ( ) .
*/
if ( resultRelInfo - > ri_GenVirtualNotNullConstraintExprs = = NULL )
{
oldContext = MemoryContextSwitchTo ( estate - > es_query_cxt ) ;
resultRelInfo - > ri_GenVirtualNotNullConstraintExprs =
palloc0_array ( ExprState * , list_length ( notnull_virtual_attrs ) ) ;
foreach_int ( attnum , notnull_virtual_attrs )
{
int i = foreach_current_index ( attnum ) ;
NullTest * nnulltest ;
/* "generated_expression IS NOT NULL" check. */
nnulltest = makeNode ( NullTest ) ;
nnulltest - > arg = ( Expr * ) build_generation_expression ( rel , attnum ) ;
nnulltest - > nulltesttype = IS_NOT_NULL ;
nnulltest - > argisrow = false ;
nnulltest - > location = - 1 ;
resultRelInfo - > ri_GenVirtualNotNullConstraintExprs [ i ] =
ExecPrepareExpr ( ( Expr * ) nnulltest , estate ) ;
}
MemoryContextSwitchTo ( oldContext ) ;
}
/*
* We will use the EState ' s per - tuple context for evaluating virtual
* generated column not null constraint expressions ( creating it if it ' s
* not already there ) .
*/
econtext = GetPerTupleExprContext ( estate ) ;
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext - > ecxt_scantuple = slot ;
/* And evaluate the check constraints for virtual generated column */
foreach_int ( attnum , notnull_virtual_attrs )
{
int i = foreach_current_index ( attnum ) ;
ExprState * exprstate = resultRelInfo - > ri_GenVirtualNotNullConstraintExprs [ i ] ;
Assert ( exprstate ! = NULL ) ;
if ( ! ExecCheck ( exprstate , econtext ) )
return attnum ;
}
/* InvalidAttrNumber result means no error */
return InvalidAttrNumber ;
}
/*
* Report a violation of a not - null constraint that was already detected .
*/
static void
ReportNotNullViolationError ( ResultRelInfo * resultRelInfo , TupleTableSlot * slot ,
EState * estate , int attnum )
{
Bitmapset * modifiedCols ;
char * val_desc ;
Relation rel = resultRelInfo - > ri_RelationDesc ;
Relation orig_rel = rel ;
TupleDesc tupdesc = RelationGetDescr ( rel ) ;
TupleDesc orig_tupdesc = RelationGetDescr ( rel ) ;
Form_pg_attribute att = TupleDescAttr ( tupdesc , attnum - 1 ) ;
Assert ( attnum > 0 ) ;
/*
* If the tuple has been routed , it ' s been converted to the partition ' s
* rowtype , which might differ from the root table ' s . We must convert it
* back to the root table ' s rowtype so that val_desc shown error message
* matches the input tuple .
*/
if ( resultRelInfo - > ri_RootResultRelInfo )
{
ResultRelInfo * rootrel = resultRelInfo - > ri_RootResultRelInfo ;
AttrMap * map ;
tupdesc = RelationGetDescr ( rootrel - > ri_RelationDesc ) ;
/* a reverse map */
map = build_attrmap_by_name_if_req ( orig_tupdesc ,
tupdesc ,
false ) ;
/*
* Partition - specific slot ' s tupdesc can ' t be changed , so allocate a
* new one .
*/
if ( map ! = NULL )
slot = execute_attr_map_slot ( map , slot ,
MakeTupleTableSlot ( tupdesc , & TTSOpsVirtual ) ) ;
modifiedCols = bms_union ( ExecGetInsertedCols ( rootrel , estate ) ,
ExecGetUpdatedCols ( rootrel , estate ) ) ;
rel = rootrel - > ri_RelationDesc ;
}
else
modifiedCols = bms_union ( ExecGetInsertedCols ( resultRelInfo , estate ) ,
ExecGetUpdatedCols ( resultRelInfo , estate ) ) ;
val_desc = ExecBuildSlotValueDescription ( RelationGetRelid ( rel ) ,
slot ,
tupdesc ,
modifiedCols ,
64 ) ;
ereport ( ERROR ,
errcode ( ERRCODE_NOT_NULL_VIOLATION ) ,
errmsg ( " null value in column \" %s \" of relation \" %s \" violates not-null constraint " ,
NameStr ( att - > attname ) ,
RelationGetRelationName ( orig_rel ) ) ,
val_desc ? errdetail ( " Failing row contains %s. " , val_desc ) : 0 ,
errtablecol ( orig_rel , attnum ) ) ;
}
/*
/*
* ExecWithCheckOptions - - check that tuple satisfies any WITH CHECK OPTIONs
* ExecWithCheckOptions - - check that tuple satisfies any WITH CHECK OPTIONs
* of the specified kind .
* of the specified kind .