88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.136 2009/11/04 22:26:05 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.137 2009/12/14 02:15:51 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -338,7 +338,7 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
338338fcache -> returnsTuple = check_sql_fn_retval (foid ,
339339rettype ,
340340queryTree_list ,
341- false ,
341+ NULL ,
342342& fcache -> junkFilter );
343343
344344if (fcache -> returnsTuple )
@@ -1003,14 +1003,27 @@ ShutdownSQLFunction(Datum arg)
10031003 * function definition of a polymorphic function.)
10041004 *
10051005 * This function returns true if the sql function returns the entire tuple
1006- * result of its final statement,and falseotherwise.Note that because we
1007- *allow "SELECT rowtype_expression", this may be false even when thedeclared
1008- *function returntype is a rowtype .
1006+ * result of its final statement,or falseif it returns just the first column
1007+ *result of that statement. It throws an error if thefinal statement doesn't
1008+ * returnthe right type at all .
10091009 *
1010- * If insertRelabels is true, then binary-compatible cases are dealt with
1011- * by actually inserting RelabelType nodes into the output targetlist;
1012- * obviously the caller must pass a parsetree that it's okay to modify in this
1013- * case.
1010+ * Note that because we allow "SELECT rowtype_expression", the result can be
1011+ * false even when the declared function return type is a rowtype.
1012+ *
1013+ * If modifyTargetList isn't NULL, the function will modify the final
1014+ * statement's targetlist in two cases:
1015+ * (1) if the tlist returns values that are binary-coercible to the expected
1016+ * type rather than being exactly the expected type. RelabelType nodes will
1017+ * be inserted to make the result types match exactly.
1018+ * (2) if there are dropped columns in the declared result rowtype. NULL
1019+ * output columns will be inserted in the tlist to match them.
1020+ * (Obviously the caller must pass a parsetree that is okay to modify when
1021+ * using this flag.) Note that this flag does not affect whether the tlist is
1022+ * considered to be a legal match to the result type, only how we react to
1023+ * allowed not-exact-match cases. *modifyTargetList will be set true iff
1024+ * we had to make any "dangerous" changes that could modify the semantics of
1025+ * the statement. If it is set true, the caller should not use the modified
1026+ * statement, but for simplicity we apply the changes anyway.
10141027 *
10151028 * If junkFilter isn't NULL, then *junkFilter is set to a JunkFilter defined
10161029 * to convert the function's tuple result to the correct output tuple type.
@@ -1019,10 +1032,11 @@ ShutdownSQLFunction(Datum arg)
10191032 */
10201033bool
10211034check_sql_fn_retval (Oid func_id ,Oid rettype ,List * queryTreeList ,
1022- bool insertRelabels ,
1035+ bool * modifyTargetList ,
10231036JunkFilter * * junkFilter )
10241037{
10251038Query * parse ;
1039+ List * * tlist_ptr ;
10261040List * tlist ;
10271041int tlistlen ;
10281042char fn_typtype ;
@@ -1031,6 +1045,8 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
10311045
10321046AssertArg (!IsPolymorphicType (rettype ));
10331047
1048+ if (modifyTargetList )
1049+ * modifyTargetList = false;/* initialize for no change */
10341050if (junkFilter )
10351051* junkFilter = NULL ;/* initialize in case of VOID result */
10361052
@@ -1064,6 +1080,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
10641080parse -> utilityStmt == NULL &&
10651081parse -> intoClause == NULL )
10661082{
1083+ tlist_ptr = & parse -> targetList ;
10671084tlist = parse -> targetList ;
10681085}
10691086else if (parse &&
@@ -1072,6 +1089,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
10721089parse -> commandType == CMD_DELETE )&&
10731090parse -> returningList )
10741091{
1092+ tlist_ptr = & parse -> returningList ;
10751093tlist = parse -> returningList ;
10761094}
10771095else
@@ -1132,11 +1150,16 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
11321150format_type_be (rettype )),
11331151errdetail ("Actual return type is %s." ,
11341152format_type_be (restype ))));
1135- if (insertRelabels && restype != rettype )
1153+ if (modifyTargetList && restype != rettype )
1154+ {
11361155tle -> expr = (Expr * )makeRelabelType (tle -> expr ,
11371156rettype ,
11381157-1 ,
11391158COERCE_DONTCARE );
1159+ /* Relabel is dangerous if TLE is a sort/group or setop column */
1160+ if (tle -> ressortgroupref != 0 || parse -> setOperations )
1161+ * modifyTargetList = true;
1162+ }
11401163
11411164/* Set up junk filter if needed */
11421165if (junkFilter )
@@ -1149,6 +1172,8 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
11491172int tupnatts ;/* physical number of columns in tuple */
11501173int tuplogcols ;/* # of nondeleted columns in tuple */
11511174int colindex ;/* physical column index */
1175+ List * newtlist ;/* new non-junk tlist entries */
1176+ List * junkattrs ;/* new junk tlist entries */
11521177
11531178/*
11541179 * If the target list is of length 1, and the type of the varnode in
@@ -1165,11 +1190,16 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
11651190restype = exprType ((Node * )tle -> expr );
11661191if (IsBinaryCoercible (restype ,rettype ))
11671192{
1168- if (insertRelabels && restype != rettype )
1193+ if (modifyTargetList && restype != rettype )
1194+ {
11691195tle -> expr = (Expr * )makeRelabelType (tle -> expr ,
11701196rettype ,
11711197-1 ,
11721198COERCE_DONTCARE );
1199+ /* Relabel is dangerous if sort/group or setop column */
1200+ if (tle -> ressortgroupref != 0 || parse -> setOperations )
1201+ * modifyTargetList = true;
1202+ }
11731203/* Set up junk filter if needed */
11741204if (junkFilter )
11751205* junkFilter = ExecInitJunkFilter (tlist , false,NULL );
@@ -1193,11 +1223,14 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
11931223/*
11941224 * Verify that the targetlist matches the return tuple type. We scan
11951225 * the non-deleted attributes to ensure that they match the datatypes
1196- * of the non-resjunk columns.
1226+ * of the non-resjunk columns. For deleted attributes, insert NULL
1227+ * result columns if the caller asked for that.
11971228 */
11981229tupnatts = tupdesc -> natts ;
11991230tuplogcols = 0 ;/* we'll count nondeleted cols as we go */
12001231colindex = 0 ;
1232+ newtlist = NIL ;/* these are only used if modifyTargetList */
1233+ junkattrs = NIL ;
12011234
12021235foreach (lc ,tlist )
12031236{
@@ -1207,7 +1240,11 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
12071240Oid atttype ;
12081241
12091242if (tle -> resjunk )
1243+ {
1244+ if (modifyTargetList )
1245+ junkattrs = lappend (junkattrs ,tle );
12101246continue ;
1247+ }
12111248
12121249do
12131250{
@@ -1219,6 +1256,26 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
12191256format_type_be (rettype )),
12201257errdetail ("Final statement returns too many columns." )));
12211258attr = tupdesc -> attrs [colindex - 1 ];
1259+ if (attr -> attisdropped && modifyTargetList )
1260+ {
1261+ Expr * null_expr ;
1262+
1263+ /* The type of the null we insert isn't important */
1264+ null_expr = (Expr * )makeConst (INT4OID ,
1265+ -1 ,
1266+ sizeof (int32 ),
1267+ (Datum )0 ,
1268+ true,/* isnull */
1269+ true/* byval */ );
1270+ newtlist = lappend (newtlist ,
1271+ makeTargetEntry (null_expr ,
1272+ colindex ,
1273+ NULL ,
1274+ false));
1275+ /* NULL insertion is dangerous in a setop */
1276+ if (parse -> setOperations )
1277+ * modifyTargetList = true;
1278+ }
12221279}while (attr -> attisdropped );
12231280tuplogcols ++ ;
12241281
@@ -1233,28 +1290,66 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
12331290format_type_be (tletype ),
12341291format_type_be (atttype ),
12351292tuplogcols )));
1236- if (insertRelabels && tletype != atttype )
1237- tle -> expr = (Expr * )makeRelabelType (tle -> expr ,
1238- atttype ,
1239- -1 ,
1240- COERCE_DONTCARE );
1293+ if (modifyTargetList )
1294+ {
1295+ if (tletype != atttype )
1296+ {
1297+ tle -> expr = (Expr * )makeRelabelType (tle -> expr ,
1298+ atttype ,
1299+ -1 ,
1300+ COERCE_DONTCARE );
1301+ /* Relabel is dangerous if sort/group or setop column */
1302+ if (tle -> ressortgroupref != 0 || parse -> setOperations )
1303+ * modifyTargetList = true;
1304+ }
1305+ tle -> resno = colindex ;
1306+ newtlist = lappend (newtlist ,tle );
1307+ }
12411308}
12421309
1243- for (;;)
1310+ /* remaining columns in tupdesc had better all be dropped */
1311+ for (colindex ++ ;colindex <=tupnatts ;colindex ++ )
12441312{
1245- colindex ++ ;
1246- if (colindex > tupnatts )
1247- break ;
12481313if (!tupdesc -> attrs [colindex - 1 ]-> attisdropped )
1249- tuplogcols ++ ;
1314+ ereport (ERROR ,
1315+ (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION ),
1316+ errmsg ("return type mismatch in function declared to return %s" ,
1317+ format_type_be (rettype )),
1318+ errdetail ("Final statement returns too few columns." )));
1319+ if (modifyTargetList )
1320+ {
1321+ Expr * null_expr ;
1322+
1323+ /* The type of the null we insert isn't important */
1324+ null_expr = (Expr * )makeConst (INT4OID ,
1325+ -1 ,
1326+ sizeof (int32 ),
1327+ (Datum )0 ,
1328+ true,/* isnull */
1329+ true/* byval */ );
1330+ newtlist = lappend (newtlist ,
1331+ makeTargetEntry (null_expr ,
1332+ colindex ,
1333+ NULL ,
1334+ false));
1335+ /* NULL insertion is dangerous in a setop */
1336+ if (parse -> setOperations )
1337+ * modifyTargetList = true;
1338+ }
12501339}
12511340
1252- if (tlistlen != tuplogcols )
1253- ereport (ERROR ,
1254- (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION ),
1255- errmsg ("return type mismatch in function declared to return %s" ,
1256- format_type_be (rettype )),
1257- errdetail ("Final statement returns too few columns." )));
1341+ if (modifyTargetList )
1342+ {
1343+ /* ensure resjunk columns are numbered correctly */
1344+ foreach (lc ,junkattrs )
1345+ {
1346+ TargetEntry * tle = (TargetEntry * )lfirst (lc );
1347+
1348+ tle -> resno = colindex ++ ;
1349+ }
1350+ /* replace the tlist with the modified one */
1351+ * tlist_ptr = list_concat (newtlist ,junkattrs );
1352+ }
12581353
12591354/* Set up junk filter if needed */
12601355if (junkFilter )