26
26
*
27
27
*
28
28
* 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 $
30
30
*
31
31
*-------------------------------------------------------------------------
32
32
*/
47
47
#include "miscadmin.h"
48
48
#include "optimizer/clauses.h"
49
49
#include "parser/parse_clause.h"
50
+ #include "parser/parse_expr.h"
50
51
#include "parser/parsetree.h"
51
52
#include "storage/bufmgr.h"
52
53
#include "storage/lmgr.h"
53
54
#include "storage/smgr.h"
54
55
#include "utils/acl.h"
56
+ #include "utils/builtins.h"
55
57
#include "utils/lsyscache.h"
56
58
#include "utils/memutils.h"
57
59
#include "utils/snapmgr.h"
@@ -72,6 +74,7 @@ typedef struct evalPlanQual
72
74
73
75
/* decls for local routines only used within this module */
74
76
static void InitPlan (QueryDesc * queryDesc , int eflags );
77
+ static void ExecCheckPlanOutput (Relation resultRel , List * targetList );
75
78
static void ExecEndPlan (PlanState * planstate , EState * estate );
76
79
static TupleTableSlot * ExecutePlan (EState * estate , PlanState * planstate ,
77
80
CmdType operation ,
@@ -697,6 +700,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
697
700
* filter if there are any junk attrs in the tlist. UPDATE and
698
701
* DELETE always need a filter, since there's always a junk 'ctid'
699
702
* 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).
700
706
*/
701
707
{
702
708
bool junk_filter_needed = false;
@@ -751,6 +757,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
751
757
PlanState * subplan = appendplans [i ];
752
758
JunkFilter * j ;
753
759
760
+ if (operation == CMD_UPDATE )
761
+ ExecCheckPlanOutput (resultRelInfo -> ri_RelationDesc ,
762
+ subplan -> plan -> targetlist );
763
+
754
764
j = ExecInitJunkFilter (subplan -> plan -> targetlist ,
755
765
resultRelInfo -> ri_RelationDesc -> rd_att -> tdhasoid ,
756
766
ExecAllocTableSlot (estate -> es_tupleTable ));
@@ -791,6 +801,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
791
801
/* Normal case with just one JunkFilter */
792
802
JunkFilter * j ;
793
803
804
+ if (operation == CMD_INSERT || operation == CMD_UPDATE )
805
+ ExecCheckPlanOutput (estate -> es_result_relation_info -> ri_RelationDesc ,
806
+ planstate -> plan -> targetlist );
807
+
794
808
j = ExecInitJunkFilter (planstate -> plan -> targetlist ,
795
809
tupType -> tdhasoid ,
796
810
ExecAllocTableSlot (estate -> es_tupleTable ));
@@ -827,6 +841,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
827
841
}
828
842
else
829
843
{
844
+ if (operation == CMD_INSERT )
845
+ ExecCheckPlanOutput (estate -> es_result_relation_info -> ri_RelationDesc ,
846
+ planstate -> plan -> targetlist );
847
+
830
848
estate -> es_junkFilter = NULL ;
831
849
if (estate -> es_rowMarks )
832
850
elog (ERROR , "SELECT FOR UPDATE/SHARE, but no junk columns" );
@@ -974,6 +992,75 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
974
992
ExecOpenIndices (resultRelInfo );
975
993
}
976
994
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
+
977
1064
/*
978
1065
* ExecGetTriggerResultRel
979
1066
*
0 commit comments