@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / executor / execMain . c , v 1.311 2008 / 07 / 26 19 : 15 : 35 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / executor / execMain . c , v 1.312 2008 / 08 / 08 17 : 01 : 11 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -47,11 +47,13 @@
# include "miscadmin.h"
# include "optimizer/clauses.h"
# include "parser/parse_clause.h"
# include "parser/parse_expr.h"
# include "parser/parsetree.h"
# include "storage/bufmgr.h"
# include "storage/lmgr.h"
# include "storage/smgr.h"
# include "utils/acl.h"
# include "utils/builtins.h"
# include "utils/lsyscache.h"
# include "utils/memutils.h"
# include "utils/snapmgr.h"
@ -72,6 +74,7 @@ typedef struct evalPlanQual
/* decls for local routines only used within this module */
static void InitPlan ( QueryDesc * queryDesc , int eflags ) ;
static void ExecCheckPlanOutput ( Relation resultRel , List * targetList ) ;
static void ExecEndPlan ( PlanState * planstate , EState * estate ) ;
static TupleTableSlot * ExecutePlan ( EState * estate , PlanState * planstate ,
CmdType operation ,
@ -697,6 +700,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* filter if there are any junk attrs in the tlist . UPDATE and
* DELETE always need a filter , since there ' s always a junk ' ctid '
* attribute present - - - no need to look first .
*
* This section of code is also a convenient place to verify that the
* output of an INSERT or UPDATE matches the target table ( s ) .
*/
{
bool junk_filter_needed = false ;
@ -751,6 +757,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
PlanState * subplan = appendplans [ i ] ;
JunkFilter * j ;
if ( operation = = CMD_UPDATE )
ExecCheckPlanOutput ( resultRelInfo - > ri_RelationDesc ,
subplan - > plan - > targetlist ) ;
j = ExecInitJunkFilter ( subplan - > plan - > targetlist ,
resultRelInfo - > ri_RelationDesc - > rd_att - > tdhasoid ,
ExecAllocTableSlot ( estate - > es_tupleTable ) ) ;
@ -791,6 +801,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
/* Normal case with just one JunkFilter */
JunkFilter * j ;
if ( operation = = CMD_INSERT | | operation = = CMD_UPDATE )
ExecCheckPlanOutput ( estate - > es_result_relation_info - > ri_RelationDesc ,
planstate - > plan - > targetlist ) ;
j = ExecInitJunkFilter ( planstate - > plan - > targetlist ,
tupType - > tdhasoid ,
ExecAllocTableSlot ( estate - > es_tupleTable ) ) ;
@ -827,6 +841,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
}
else
{
if ( operation = = CMD_INSERT )
ExecCheckPlanOutput ( estate - > es_result_relation_info - > ri_RelationDesc ,
planstate - > plan - > targetlist ) ;
estate - > es_junkFilter = NULL ;
if ( estate - > es_rowMarks )
elog ( ERROR , " SELECT FOR UPDATE/SHARE, but no junk columns " ) ;
@ -974,6 +992,75 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
ExecOpenIndices ( resultRelInfo ) ;
}
/*
* Verify that the tuples to be produced by INSERT or UPDATE match the
* target relation ' s rowtype
*
* We do this to guard against stale plans . If plan invalidation is
* functioning properly then we should never get a failure here , but better
* safe than sorry . Note that this is called after we have obtained lock
* on the target rel , so the rowtype can ' t change underneath us .
*
* The plan output is represented by its targetlist , because that makes
* handling the dropped - column case easier .
*/
static void
ExecCheckPlanOutput ( Relation resultRel , List * targetList )
{
TupleDesc resultDesc = RelationGetDescr ( resultRel ) ;
int attno = 0 ;
ListCell * lc ;
foreach ( lc , targetList )
{
TargetEntry * tle = ( TargetEntry * ) lfirst ( lc ) ;
Form_pg_attribute attr ;
if ( tle - > resjunk )
continue ; /* ignore junk tlist items */
if ( attno > = resultDesc - > natts )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail ( " Query has too many columns. " ) ) ) ;
attr = resultDesc - > attrs [ attno + + ] ;
if ( ! attr - > attisdropped )
{
/* Normal case: demand type match */
if ( exprType ( ( Node * ) tle - > expr ) ! = attr - > atttypid )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail ( " Table has type %s at ordinal position %d, but query expects %s. " ,
format_type_be ( attr - > atttypid ) ,
attno ,
format_type_be ( exprType ( ( Node * ) tle - > expr ) ) ) ) ) ;
}
else
{
/*
* For a dropped column , we can ' t check atttypid ( it ' s likely 0 ) .
* In any case the planner has most likely inserted an INT4 null .
* What we insist on is just * some * NULL constant .
*/
if ( ! IsA ( tle - > expr , Const ) | |
! ( ( Const * ) tle - > expr ) - > constisnull )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail ( " Query provides a value for a dropped column at ordinal position %d. " ,
attno ) ) ) ;
}
}
if ( attno ! = resultDesc - > natts )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail ( " Query has too few columns. " ) ) ) ;
}
/*
* ExecGetTriggerResultRel
*