Hi all,

The Oslandia team is involved in PostGIS project for years, with a
current focus on PostGIS 3D support.
With PostGIS queries, nested functions calls that manipulate geometries
are quite common, e.g.: SELECT ST_Union(ST_Intersection(a.geom,
ST_Buffer(b.geom, 50)))

PostGIS functions that manipulate geometries have to unserialize their
input geometries from the 'flat' varlena representation to their own,
and serialize the processed geometries back when returning.
But in such nested call queries, this serialization-unserialization
process is just an overhead.

Avoiding it could then lead to a real gain in terms of performances [1],
especially here when the internal type takes time to serialize (and with
new PostGIS types like rasters or 3D geometries it's really meaningful)

So we thought having a way for user functions to know if they are part
of a nested call could allow them to avoid this serialization phase.

The idea would be to have a boolean flag reachable from a user function
(within FunctionCallInfoData) that says if the current function is
nested or not.

We already investigated such a modification and here is where we are up
to now :
  - we modified the parser with a new boolean member 'nested' to the
FuncExpr struct. Within the parser, we know if a function call is nested
into another one and then we can mark the FuncExpr
  - the executor has been modified so it can take into account this
nested member and pass it to the FunctionCallInfoData structure before
evaluating the function

We are working on a PostGIS branch that takes benefit of this
functionality [2]

You can find in attachment a first draft of the patch.

Obviously, even if this is about a PostGIS use case here, this subject
could be helpful for every other queries using both nested functions and
serialization.

I am quite new to postgresql hacking, so I'm sure there is room for
improvements. But, what about this first proposal ?

I'll be at the PGDay conf in Dublin next week, so we could discuss this
topic.

[1] Talking about performances, we already investigated such
"pass-by-reference" mechanism with PostGIS. Taking a dummy function
"st_copy" that only copies its input geometry to its output with 4
levels of nesting gives encouraging results (passing geometries by
reference is more than 2x faster than (un)serializing) :
https://github.com/Oslandia/sfcgal-tests/blob/master/bench/report_serialization_referenced_vs_native.pdf

[2] https://github.com/Oslandia/postgis/tree/nested_ref_passing
-- 
Hugo Mercier
Oslandia
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 90c2753..56c701f 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -182,6 +182,8 @@ static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 					  bool *isNull, ExprDoneCond *isDone);
 
+static ExprState * ExecInitExprRec(Expr *node, PlanState *parent, Expr *parentNode);
+
 
 /* ----------------------------------------------------------------
  *		ExecEvalExpr routines
@@ -4303,7 +4305,6 @@ ExecEvalExprSwitchContext(ExprState *expression,
 	return retDatum;
 }
 
-
 /*
  * ExecInitExpr: prepare an expression tree for execution
  *
@@ -4336,9 +4337,21 @@ ExecEvalExprSwitchContext(ExprState *expression,
  * associated with a plan tree.  (If so, it can't have aggs or subplans.)
  * This case should usually come through ExecPrepareExpr, not directly here.
  */
+
 ExprState *
 ExecInitExpr(Expr *node, PlanState *parent)
 {
+    return ExecInitExprRec(node, parent, NULL);
+}
+
+/*
+ * ExecInitExprRec: used by ExecInitExpr
+ *
+ * The additional parameter 'parentNode' is the expression node parent of 'node'.
+ */
+static ExprState *
+ExecInitExprRec(Expr *node, PlanState *parent, Expr *parentNode )
+{
 	ExprState  *state;
 
 	if (node == NULL)
@@ -4408,10 +4421,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					aggstate->aggs = lcons(astate, aggstate->aggs);
 					naggs = ++aggstate->numaggs;
 
-					astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
-														 parent);
-					astate->aggfilter = ExecInitExpr(aggref->aggfilter,
-													 parent);
+					astate->args = (List *) ExecInitExprRec((Expr *) aggref->args,
+															parent, node);
+					astate->aggfilter = ExecInitExprRec(aggref->aggfilter,
+														parent, node);
 
 					/*
 					 * Complain if the aggregate's arguments contain any
@@ -4448,10 +4461,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					if (wfunc->winagg)
 						winstate->numaggs++;
 
-					wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
-														  parent);
-					wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
-													  parent);
+					wfstate->args = (List *) ExecInitExprRec((Expr *) wfunc->args,
+															 parent, node);
+					wfstate->aggfilter = ExecInitExprRec(wfunc->aggfilter,
+														 parent, node);
 
 					/*
 					 * Complain if the windowfunc's arguments contain any
@@ -4479,12 +4492,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
 
 				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
 				astate->refupperindexpr = (List *)
-					ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+				    ExecInitExprRec((Expr *) aref->refupperindexpr, parent, node);
 				astate->reflowerindexpr = (List *)
-					ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
-				astate->refexpr = ExecInitExpr(aref->refexpr, parent);
-				astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
-													parent);
+				    ExecInitExprRec((Expr *) aref->reflowerindexpr, parent, node);
+				astate->refexpr = ExecInitExprRec(aref->refexpr, parent, node);
+				astate->refassgnexpr = ExecInitExprRec(aref->refassgnexpr,
+								       parent, node);
 				/* do one-time catalog lookups for type info */
 				astate->refattrlength = get_typlen(aref->refarraytype);
 				get_typlenbyvalalign(aref->refelemtype,
@@ -4499,9 +4512,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				FuncExpr   *funcexpr = (FuncExpr *) node;
 				FuncExprState *fstate = makeNode(FuncExprState);
 
+				fstate->fcinfo_data.nested = funcexpr->nested;
 				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
 				fstate->args = (List *)
-					ExecInitExpr((Expr *) funcexpr->args, parent);
+				    ExecInitExprRec((Expr *) funcexpr->args, parent, node);
 				fstate->func.fn_oid = InvalidOid;		/* not initialized */
 				state = (ExprState *) fstate;
 			}
@@ -4513,7 +4527,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 
 				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
 				fstate->args = (List *)
-					ExecInitExpr((Expr *) opexpr->args, parent);
+				    ExecInitExprRec((Expr *) opexpr->args, parent, node);
 				fstate->func.fn_oid = InvalidOid;		/* not initialized */
 				state = (ExprState *) fstate;
 			}
@@ -4525,7 +4539,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 
 				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
 				fstate->args = (List *)
-					ExecInitExpr((Expr *) distinctexpr->args, parent);
+				    ExecInitExprRec((Expr *) distinctexpr->args, parent, node);
 				fstate->func.fn_oid = InvalidOid;		/* not initialized */
 				state = (ExprState *) fstate;
 			}
@@ -4537,7 +4551,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 
 				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
 				fstate->args = (List *)
-					ExecInitExpr((Expr *) nullifexpr->args, parent);
+				    ExecInitExprRec((Expr *) nullifexpr->args, parent, node);
 				fstate->func.fn_oid = InvalidOid;		/* not initialized */
 				state = (ExprState *) fstate;
 			}
@@ -4549,7 +4563,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 
 				sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
 				sstate->fxprstate.args = (List *)
-					ExecInitExpr((Expr *) opexpr->args, parent);
+				    ExecInitExprRec((Expr *) opexpr->args, parent, node);
 				sstate->fxprstate.func.fn_oid = InvalidOid;		/* not initialized */
 				sstate->element_type = InvalidOid;		/* ditto */
 				state = (ExprState *) sstate;
@@ -4577,7 +4591,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 						break;
 				}
 				bstate->args = (List *)
-					ExecInitExpr((Expr *) boolexpr->args, parent);
+				    ExecInitExprRec((Expr *) boolexpr->args, parent, node);
 				state = (ExprState *) bstate;
 			}
 			break;
@@ -4616,7 +4630,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				FieldSelectState *fstate = makeNode(FieldSelectState);
 
 				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
-				fstate->arg = ExecInitExpr(fselect->arg, parent);
+				fstate->arg = ExecInitExprRec(fselect->arg, parent, node);
 				fstate->argdesc = NULL;
 				state = (ExprState *) fstate;
 			}
@@ -4627,8 +4641,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				FieldStoreState *fstate = makeNode(FieldStoreState);
 
 				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
-				fstate->arg = ExecInitExpr(fstore->arg, parent);
-				fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
+				fstate->arg = ExecInitExprRec(fstore->arg, parent, node);
+				fstate->newvals = (List *) ExecInitExprRec((Expr *) fstore->newvals, parent, node);
 				fstate->argdesc = NULL;
 				state = (ExprState *) fstate;
 			}
@@ -4639,7 +4653,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				GenericExprState *gstate = makeNode(GenericExprState);
 
 				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
-				gstate->arg = ExecInitExpr(relabel->arg, parent);
+				gstate->arg = ExecInitExprRec(relabel->arg, parent, node);
 				state = (ExprState *) gstate;
 			}
 			break;
@@ -4651,7 +4665,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				bool		typisvarlena;
 
 				iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
-				iostate->arg = ExecInitExpr(iocoerce->arg, parent);
+				iostate->arg = ExecInitExprRec(iocoerce->arg, parent, node);
 				/* lookup the result type's input function */
 				getTypeInputInfo(iocoerce->resulttype, &iofunc,
 								 &iostate->intypioparam);
@@ -4669,7 +4683,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
 
 				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
-				astate->arg = ExecInitExpr(acoerce->arg, parent);
+				astate->arg = ExecInitExprRec(acoerce->arg, parent, node);
 				astate->resultelemtype = get_element_type(acoerce->resulttype);
 				if (astate->resultelemtype == InvalidOid)
 					ereport(ERROR,
@@ -4689,7 +4703,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
 
 				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
-				cstate->arg = ExecInitExpr(convert->arg, parent);
+				cstate->arg = ExecInitExprRec(convert->arg, parent, node);
 				state = (ExprState *) cstate;
 			}
 			break;
@@ -4701,7 +4715,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				ListCell   *l;
 
 				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
-				cstate->arg = ExecInitExpr(caseexpr->arg, parent);
+				cstate->arg = ExecInitExprRec(caseexpr->arg, parent, node);
 				foreach(l, caseexpr->args)
 				{
 					CaseWhen   *when = (CaseWhen *) lfirst(l);
@@ -4710,12 +4724,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Assert(IsA(when, CaseWhen));
 					wstate->xprstate.evalfunc = NULL;	/* not used */
 					wstate->xprstate.expr = (Expr *) when;
-					wstate->expr = ExecInitExpr(when->expr, parent);
-					wstate->result = ExecInitExpr(when->result, parent);
+					wstate->expr = ExecInitExprRec(when->expr, parent, node);
+					wstate->result = ExecInitExprRec(when->result, parent, node);
 					outlist = lappend(outlist, wstate);
 				}
 				cstate->args = outlist;
-				cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
+				cstate->defresult = ExecInitExprRec(caseexpr->defresult, parent, node);
 				state = (ExprState *) cstate;
 			}
 			break;
@@ -4732,7 +4746,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(l);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				astate->elements = outlist;
@@ -4801,7 +4815,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 						 */
 						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
 					}
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 					i++;
 				}
@@ -4828,7 +4842,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(l);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				rstate->largs = outlist;
@@ -4839,7 +4853,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(l);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				rstate->rargs = outlist;
@@ -4892,7 +4906,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(l);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				cstate->args = outlist;
@@ -4913,7 +4927,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(l);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				mstate->args = outlist;
@@ -4950,7 +4964,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(arg);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				xstate->named_args = outlist;
@@ -4961,7 +4975,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					Expr	   *e = (Expr *) lfirst(arg);
 					ExprState  *estate;
 
-					estate = ExecInitExpr(e, parent);
+					estate = ExecInitExprRec(e, parent, node);
 					outlist = lappend(outlist, estate);
 				}
 				xstate->args = outlist;
@@ -4975,7 +4989,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				NullTestState *nstate = makeNode(NullTestState);
 
 				nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
-				nstate->arg = ExecInitExpr(ntest->arg, parent);
+				nstate->arg = ExecInitExprRec(ntest->arg, parent, node);
 				nstate->argdesc = NULL;
 				state = (ExprState *) nstate;
 			}
@@ -4986,7 +5000,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				GenericExprState *gstate = makeNode(GenericExprState);
 
 				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
-				gstate->arg = ExecInitExpr(btest->arg, parent);
+				gstate->arg = ExecInitExprRec(btest->arg, parent, node);
 				state = (ExprState *) gstate;
 			}
 			break;
@@ -4996,7 +5010,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
 
 				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
-				cstate->arg = ExecInitExpr(ctest->arg, parent);
+				cstate->arg = ExecInitExprRec(ctest->arg, parent, node);
 				cstate->constraints = GetDomainConstraints(ctest->resulttype);
 				state = (ExprState *) cstate;
 			}
@@ -5011,7 +5025,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				GenericExprState *gstate = makeNode(GenericExprState);
 
 				gstate->xprstate.evalfunc = NULL;		/* not used */
-				gstate->arg = ExecInitExpr(tle->expr, parent);
+				gstate->arg = ExecInitExprRec(tle->expr, parent, node);
 				state = (ExprState *) gstate;
 			}
 			break;
@@ -5023,8 +5037,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				foreach(l, (List *) node)
 				{
 					outlist = lappend(outlist,
-									  ExecInitExpr((Expr *) lfirst(l),
-												   parent));
+									  ExecInitExprRec((Expr *) lfirst(l),
+											  parent, parentNode));
 				}
 				/* Don't fall through to the "common" code below */
 				return (ExprState *) outlist;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 65f3b98..f12d530 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1207,6 +1207,7 @@ _copyFuncExpr(const FuncExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+    COPY_SCALAR_FIELD(nested);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4c9b05e..ea4df6c 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -249,6 +249,7 @@ _equalFuncExpr(const FuncExpr *a, const FuncExpr *b)
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(nested);
 
 	return true;
 }
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index b742ec9..b3fdba7 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -495,6 +495,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args,
 	funcexpr->inputcollid = inputcollid;
 	funcexpr->args = args;
 	funcexpr->location = -1;
+    funcexpr->nested = 0;
 
 	return funcexpr;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a2903f9..7ab397c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1013,6 +1013,7 @@ _outFuncExpr(StringInfo str, const FuncExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_BOOL_FIELD(nested);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index d325bb3..58e20fb 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -563,6 +563,7 @@ _readFuncExpr(void)
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
 	READ_LOCATION_FIELD(location);
+	READ_BOOL_FIELD(nested);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 76c032c..b8ae7fb 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -111,6 +111,7 @@ static Expr *simplify_function(Oid funcid,
 				  Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List **args_p,
 				  bool funcvariadic, bool process_args, bool allow_non_const,
+                  bool nested,
 				  eval_const_expressions_context *context);
 static List *expand_function_arguments(List *args, Oid result_type,
 						  HeapTuple func_tuple);
@@ -123,6 +124,7 @@ static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List *args,
 				  bool funcvariadic,
 				  HeapTuple func_tuple,
+                  bool nested,
 				  eval_const_expressions_context *context);
 static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
 				Oid input_collid, List *args,
@@ -2274,9 +2276,13 @@ eval_const_expressions_mutator(Node *node,
 										   expr->funcvariadic,
 										   true,
 										   true,
+                                           expr->nested,
 										   context);
 				if (simple)		/* successfully simplified it */
+                {
+					((FuncExpr*)simple)->nested = expr->nested;
 					return (Node *) simple;
+				}
 
 				/*
 				 * The expression cannot be simplified any further, so build
@@ -2294,6 +2300,7 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->inputcollid = expr->inputcollid;
 				newexpr->args = args;
 				newexpr->location = expr->location;
+				newexpr->nested = expr->nested;
 				return (Node *) newexpr;
 			}
 		case T_OpExpr:
@@ -2321,6 +2328,7 @@ eval_const_expressions_mutator(Node *node,
 										   false,
 										   true,
 										   true,
+										   /* nested */ false,
 										   context);
 				if (simple)		/* successfully simplified it */
 					return (Node *) simple;
@@ -2425,6 +2433,7 @@ eval_const_expressions_mutator(Node *node,
 											   false,
 											   false,
 											   false,
+											   /* nested */ false,
 											   context);
 					if (simple) /* successfully simplified it */
 					{
@@ -2629,6 +2638,7 @@ eval_const_expressions_mutator(Node *node,
 										   false,
 										   true,
 										   true,
+										   /* nested */ false,
 										   context);
 				if (simple)		/* successfully simplified output fn */
 				{
@@ -2661,6 +2671,7 @@ eval_const_expressions_mutator(Node *node,
 											   false,
 											   false,
 											   true,
+											   /* nested */ false,
 											   context);
 					if (simple) /* successfully simplified input fn */
 						return (Node *) simple;
@@ -3529,6 +3540,7 @@ static Expr *
 simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List **args_p,
 				  bool funcvariadic, bool process_args, bool allow_non_const,
+                  bool nested,
 				  eval_const_expressions_context *context)
 {
 	List	   *args = *args_p;
@@ -3574,7 +3586,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 	newexpr = evaluate_function(funcid, result_type, result_typmod,
 								result_collid, input_collid,
 								args, funcvariadic,
-								func_tuple, context);
+                                    func_tuple, nested, context);
 
 	if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
 	{
@@ -3845,6 +3857,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List *args,
 				  bool funcvariadic,
 				  HeapTuple func_tuple,
+                  bool nested,
 				  eval_const_expressions_context *context)
 {
 	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
@@ -3931,6 +3944,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 	newexpr->inputcollid = input_collid;
 	newexpr->args = args;
 	newexpr->location = -1;
+        newexpr->nested = nested;
 
 	return evaluate_expr((Expr *) newexpr, result_type, result_typmod,
 						 result_collid);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 68b711d..7c55ff9 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -39,6 +39,7 @@
 bool		Transform_null_equals = false;
 
 static Node *transformExprRecurse(ParseState *pstate, Node *expr);
+static Node *transformExprRec(ParseState *pstate, Node *expr, bool nested);
 static Node *transformParamRef(ParseState *pstate, ParamRef *pref);
 static Node *transformAExprOp(ParseState *pstate, A_Expr *a);
 static Node *transformAExprAnd(ParseState *pstate, A_Expr *a);
@@ -50,7 +51,7 @@ static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a);
 static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a);
 static Node *transformAExprOf(ParseState *pstate, A_Expr *a);
 static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
-static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
+static Node *transformFuncCall(ParseState *pstate, FuncCall *fn, bool nested);
 static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
 static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
 static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
@@ -122,7 +123,7 @@ transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
 }
 
 static Node *
-transformExprRecurse(ParseState *pstate, Node *expr)
+transformExprRec(ParseState *pstate, Node *expr, bool nested)
 {
 	Node	   *result;
 
@@ -155,7 +156,7 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 			{
 				A_Indirection *ind = (A_Indirection *) expr;
 
-				result = transformExprRecurse(pstate, ind->arg);
+				result = transformExprRec(pstate, ind->arg, nested);
 				result = transformIndirection(pstate, result,
 											  ind->indirection);
 				break;
@@ -259,14 +260,14 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 			}
 
 		case T_FuncCall:
-			result = transformFuncCall(pstate, (FuncCall *) expr);
+		        result = transformFuncCall(pstate, (FuncCall *) expr, nested);
 			break;
 
 		case T_NamedArgExpr:
 			{
 				NamedArgExpr *na = (NamedArgExpr *) expr;
 
-				na->arg = (Expr *) transformExprRecurse(pstate, (Node *) na->arg);
+				na->arg = (Expr *) transformExprRec(pstate, (Node *) na->arg, nested);
 				result = expr;
 				break;
 			}
@@ -303,7 +304,7 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 			{
 				NullTest   *n = (NullTest *) expr;
 
-				n->arg = (Expr *) transformExprRecurse(pstate, (Node *) n->arg);
+				n->arg = (Expr *) transformExprRec(pstate, (Node *) n->arg, nested);
 				/* the argument can be any type, so don't coerce it */
 				n->argisrow = type_is_rowtype(exprType((Node *) n->arg));
 				result = expr;
@@ -365,6 +366,12 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 	return result;
 }
 
+static Node *
+transformExprRecurse(ParseState *pstate, Node *expr )
+{
+    return transformExprRec( pstate, expr, false );
+}
+
 /*
  * helper routine for delivering "column does not exist" error message
  *
@@ -1238,18 +1245,20 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
 }
 
 static Node *
-transformFuncCall(ParseState *pstate, FuncCall *fn)
+transformFuncCall(ParseState *pstate, FuncCall *fn, bool nested)
 {
 	List	   *targs;
 	ListCell   *args;
+	Node       *node;
 	Expr	   *tagg_filter;
 
 	/* Transform the list of arguments ... */
 	targs = NIL;
 	foreach(args, fn->args)
 	{
-		targs = lappend(targs, transformExprRecurse(pstate,
-													(Node *) lfirst(args)));
+		targs = lappend(targs, transformExprRec(pstate,
+							    (Node *) lfirst(args),
+							    /* nested */ true));
 	}
 
 	/*
@@ -1263,7 +1272,7 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
 								 EXPR_KIND_FILTER, "FILTER");
 
 	/* ... and hand off to ParseFuncOrColumn */
-	return ParseFuncOrColumn(pstate,
+	node = ParseFuncOrColumn(pstate,
 							 fn->funcname,
 							 targs,
 							 fn->agg_order,
@@ -1274,6 +1283,12 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
 							 fn->over,
 							 false,
 							 fn->location);
+
+	if ( IsA(node, FuncExpr) ) {
+	    FuncExpr *fe = (FuncExpr*)(node);
+	    fe->nested = nested;
+	}
+	return node;
 }
 
 static Node *
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 2bd24c8..2ad68e6 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -413,6 +413,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		/* funccollid and inputcollid will be set by parse_collate.c */
 		funcexpr->args = fargs;
 		funcexpr->location = location;
+		funcexpr->nested = false;
 
 		retval = (Node *) funcexpr;
 	}
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 1f72e1b..5b920b0 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -71,6 +71,7 @@ typedef struct FunctionCallInfoData
 	Oid			fncollation;	/* collation for function to use */
 	bool		isnull;			/* function must set true if result is NULL */
 	short		nargs;			/* # arguments actually passed */
+    bool        nested;         /* is the function call nested in another function call ? */
 	Datum		arg[FUNC_MAX_ARGS];		/* Arguments passed to function */
 	bool		argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
 } FunctionCallInfoData;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 7918537..f974e8c 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -355,6 +355,7 @@ typedef struct FuncExpr
 	Oid			inputcollid;	/* OID of collation that function should use */
 	List	   *args;			/* arguments to the function */
 	int			location;		/* token location, or -1 if unknown */
+    bool        nested;         /* true if the function call is nested in another call */
 } FuncExpr;
 
 /*
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to