2626 *
2727 *
2828 * IDENTIFICATION
29- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.311 2008/07/26 19:15:35 tgl Exp $
29+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.312 2008/08/08 17:01:11 tgl Exp $
3030 *
3131 *-------------------------------------------------------------------------
3232 */
4747#include "miscadmin.h"
4848#include "optimizer/clauses.h"
4949#include "parser/parse_clause.h"
50+ #include "parser/parse_expr.h"
5051#include "parser/parsetree.h"
5152#include "storage/bufmgr.h"
5253#include "storage/lmgr.h"
5354#include "storage/smgr.h"
5455#include "utils/acl.h"
56+ #include "utils/builtins.h"
5557#include "utils/lsyscache.h"
5658#include "utils/memutils.h"
5759#include "utils/snapmgr.h"
@@ -72,6 +74,7 @@ typedef struct evalPlanQual
7274
7375/* decls for local routines only used within this module */
7476static void InitPlan (QueryDesc * queryDesc ,int eflags );
77+ static void ExecCheckPlanOutput (Relation resultRel ,List * targetList );
7578static void ExecEndPlan (PlanState * planstate ,EState * estate );
7679static TupleTableSlot * ExecutePlan (EState * estate ,PlanState * planstate ,
7780CmdType operation ,
@@ -697,6 +700,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
697700 * filter if there are any junk attrs in the tlist. UPDATE and
698701 * DELETE always need a filter, since there's always a junk 'ctid'
699702 * attribute present --- no need to look first.
703+ *
704+ * This section of code is also a convenient place to verify that the
705+ * output of an INSERT or UPDATE matches the target table(s).
700706 */
701707{
702708bool junk_filter_needed = false;
@@ -751,6 +757,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
751757PlanState * subplan = appendplans [i ];
752758JunkFilter * j ;
753759
760+ if (operation == CMD_UPDATE )
761+ ExecCheckPlanOutput (resultRelInfo -> ri_RelationDesc ,
762+ subplan -> plan -> targetlist );
763+
754764j = ExecInitJunkFilter (subplan -> plan -> targetlist ,
755765resultRelInfo -> ri_RelationDesc -> rd_att -> tdhasoid ,
756766ExecAllocTableSlot (estate -> es_tupleTable ));
@@ -791,6 +801,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
791801/* Normal case with just one JunkFilter */
792802JunkFilter * j ;
793803
804+ if (operation == CMD_INSERT || operation == CMD_UPDATE )
805+ ExecCheckPlanOutput (estate -> es_result_relation_info -> ri_RelationDesc ,
806+ planstate -> plan -> targetlist );
807+
794808j = ExecInitJunkFilter (planstate -> plan -> targetlist ,
795809tupType -> tdhasoid ,
796810ExecAllocTableSlot (estate -> es_tupleTable ));
@@ -827,6 +841,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
827841}
828842else
829843{
844+ if (operation == CMD_INSERT )
845+ ExecCheckPlanOutput (estate -> es_result_relation_info -> ri_RelationDesc ,
846+ planstate -> plan -> targetlist );
847+
830848estate -> es_junkFilter = NULL ;
831849if (estate -> es_rowMarks )
832850elog (ERROR ,"SELECT FOR UPDATE/SHARE, but no junk columns" );
@@ -974,6 +992,75 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
974992ExecOpenIndices (resultRelInfo );
975993}
976994
995+ /*
996+ * Verify that the tuples to be produced by INSERT or UPDATE match the
997+ * target relation's rowtype
998+ *
999+ * We do this to guard against stale plans. If plan invalidation is
1000+ * functioning properly then we should never get a failure here, but better
1001+ * safe than sorry. Note that this is called after we have obtained lock
1002+ * on the target rel, so the rowtype can't change underneath us.
1003+ *
1004+ * The plan output is represented by its targetlist, because that makes
1005+ * handling the dropped-column case easier.
1006+ */
1007+ static void
1008+ ExecCheckPlanOutput (Relation resultRel ,List * targetList )
1009+ {
1010+ TupleDesc resultDesc = RelationGetDescr (resultRel );
1011+ int attno = 0 ;
1012+ ListCell * lc ;
1013+
1014+ foreach (lc ,targetList )
1015+ {
1016+ TargetEntry * tle = (TargetEntry * )lfirst (lc );
1017+ Form_pg_attribute attr ;
1018+
1019+ if (tle -> resjunk )
1020+ continue ;/* ignore junk tlist items */
1021+
1022+ if (attno >=resultDesc -> natts )
1023+ ereport (ERROR ,
1024+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
1025+ errmsg ("table row type and query-specified row type do not match" ),
1026+ errdetail ("Query has too many columns." )));
1027+ attr = resultDesc -> attrs [attno ++ ];
1028+
1029+ if (!attr -> attisdropped )
1030+ {
1031+ /* Normal case: demand type match */
1032+ if (exprType ((Node * )tle -> expr )!= attr -> atttypid )
1033+ ereport (ERROR ,
1034+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
1035+ errmsg ("table row type and query-specified row type do not match" ),
1036+ errdetail ("Table has type %s at ordinal position %d, but query expects %s." ,
1037+ format_type_be (attr -> atttypid ),
1038+ attno ,
1039+ format_type_be (exprType ((Node * )tle -> expr )))));
1040+ }
1041+ else
1042+ {
1043+ /*
1044+ * For a dropped column, we can't check atttypid (it's likely 0).
1045+ * In any case the planner has most likely inserted an INT4 null.
1046+ * What we insist on is just *some* NULL constant.
1047+ */
1048+ if (!IsA (tle -> expr ,Const )||
1049+ !((Const * )tle -> expr )-> constisnull )
1050+ ereport (ERROR ,
1051+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
1052+ errmsg ("table row type and query-specified row type do not match" ),
1053+ errdetail ("Query provides a value for a dropped column at ordinal position %d." ,
1054+ attno )));
1055+ }
1056+ }
1057+ if (attno != resultDesc -> natts )
1058+ ereport (ERROR ,
1059+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
1060+ errmsg ("table row type and query-specified row type do not match" ),
1061+ errdetail ("Query has too few columns." )));
1062+ }
1063+
9771064/*
9781065 *ExecGetTriggerResultRel
9791066 *