diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 446319d..f30ae4e 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -85,6 +85,44 @@ add_paths_to_joinrel(PlannerInfo *root,
 	SemiAntiJoinFactors semifactors;
 	Relids		param_source_rels = NULL;
 	ListCell   *lc;
+	bool nest_loop_only = false;
+
+	/*
+	 * Should the outer path be parametrized by the inner, there's no method
+	 * to evaluate such a join.
+	 */
+	if (bms_overlap(outerrel->func_arg_relids, innerrel->relids)) {
+		return;
+	}
+
+	/*
+	 * Only nest-loop join is considered viable method for lateral function.
+	 *
+	 * Non-empty 'innerrel->func_arg_relids' isn't a sufficient condition for
+	 * NL join with parametrized function on the inner side:
+	 *
+	 * If there's no overlap, the function arguments have already been used
+	 * to construct the innerrel (no reason to use them again) or they are supplied
+	 * from higher level (and thus constant for the current join) .
+	 */
+	if (bms_overlap(innerrel->func_arg_relids, outerrel->relids)) {
+		/*
+		 * Merge join is the only method to evaluate full join, but merge join is a bad option
+		 * for lateral functions.
+		 */
+		if (jointype == JOIN_FULL) {
+			return;
+		}
+
+		/*
+		 * TODO
+		 * Probably not relevant. Verify.
+		 */
+		if (jointype == JOIN_SEMI || jointype == JOIN_ANTI) {
+			return;
+		}
+		nest_loop_only = true;
+	}
 
 	/*
 	 * Find potential mergejoin clauses.  We can skip this if we are not
@@ -92,7 +130,7 @@ add_paths_to_joinrel(PlannerInfo *root,
 	 * way of implementing a full outer join, so override enable_mergejoin if
 	 * it's a full join.
 	 */
-	if (enable_mergejoin || jointype == JOIN_FULL)
+	if ((enable_mergejoin || jointype == JOIN_FULL) && !nest_loop_only)
 		mergeclause_list = select_mergejoin_clauses(root,
 													joinrel,
 													outerrel,
@@ -151,7 +189,7 @@ add_paths_to_joinrel(PlannerInfo *root,
 	 * 1. Consider mergejoin paths where both relations must be explicitly
 	 * sorted.	Skip this if we can't mergejoin.
 	 */
-	if (mergejoin_allowed)
+	if (mergejoin_allowed && !nest_loop_only)
 		sort_inner_and_outer(root, joinrel, outerrel, innerrel,
 							 restrictlist, mergeclause_list, jointype,
 							 sjinfo, param_source_rels);
@@ -192,10 +230,13 @@ add_paths_to_joinrel(PlannerInfo *root,
 	 * before being joined.  As above, disregard enable_hashjoin for full
 	 * joins, because there may be no other alternative.
 	 */
-	if (enable_hashjoin || jointype == JOIN_FULL)
+	if ((enable_hashjoin || jointype == JOIN_FULL) && !nest_loop_only)
 		hash_inner_and_outer(root, joinrel, outerrel, innerrel,
 							 restrictlist, jointype,
 							 sjinfo, &semifactors, param_source_rels);
+
+	/* Prepare for check of function parametrization at the next higher level. */
+	joinrel->func_arg_relids = bms_union(outerrel->func_arg_relids, innerrel->func_arg_relids);
 }
 
 /*
@@ -654,6 +695,7 @@ match_unsorted_outer(PlannerInfo *root,
 	Path	   *inner_cheapest_total = innerrel->cheapest_total_path;
 	Path	   *matpath = NULL;
 	ListCell   *lc1;
+	bool nest_loop_only = bms_overlap(innerrel->func_arg_relids, outerrel->relids);
 
 	/*
 	 * Nestloop only supports inner, left, semi, and anti joins.  Also, if we
@@ -814,6 +856,9 @@ match_unsorted_outer(PlannerInfo *root,
 		if (save_jointype == JOIN_UNIQUE_OUTER)
 			continue;
 
+		if (nest_loop_only)
+			continue;
+
 		/* Look for useful mergeclauses (if any) */
 		mergeclauses = find_mergeclauses_for_pathkeys(root,
 													  outerpath->pathkeys,
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index c34b9b8..5552fa8 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -583,8 +583,10 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
 
 	/* For a nestloop, include outer relids in curOuterRels for inner side */
 	if (best_path->path.pathtype == T_NestLoop)
+	{
 		root->curOuterRels = bms_union(root->curOuterRels,
 								   best_path->outerjoinpath->parent->relids);
+	}
 
 	inner_plan = create_plan_recurse(root, best_path->innerjoinpath);
 
@@ -1663,18 +1665,32 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
 	FunctionScan *scan_plan;
 	Index		scan_relid = best_path->parent->relid;
 	RangeTblEntry *rte;
+	FuncExpr *funcExpr;
 
 	/* it should be a function base rel... */
 	Assert(scan_relid > 0);
 	rte = planner_rt_fetch(scan_relid, root);
 	Assert(rte->rtekind == RTE_FUNCTION);
 
+	/*
+	 * Ensure that vars in argument expressions get substituted in case the function participates in a join.
+	 *
+	 * (Function can only be used as inner relation of nest-loop join.)
+	 */
+	funcExpr = (FuncExpr *) rte->funcexpr;
+	if (funcExpr->args != NULL) {
+		funcExpr->args = (List *)
+			replace_nestloop_params(root, (Node *) funcExpr->args);
+	}
+
 	/* Sort clauses into best execution order */
 	scan_clauses = order_qual_clauses(root, scan_clauses);
 
 	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
 	scan_clauses = extract_actual_clauses(scan_clauses, false);
 
+
+
 	scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
 								  rte->funcexpr,
 								  rte->eref->colnames,
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index bfdd9ff..427e7e9 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -20,6 +20,7 @@
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/var.h"
 #include "utils/hsearch.h"
 
 
@@ -97,6 +98,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	rel = makeNode(RelOptInfo);
 	rel->reloptkind = reloptkind;
 	rel->relids = bms_make_singleton(relid);
+	rel->func_arg_relids = NULL;
 	rel->rows = 0;
 	rel->width = 0;
 	rel->reltargetlist = NIL;
@@ -154,6 +156,19 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 			break;
 	}
 
+	/*
+	 * If function arguments reference other relations, we need to keep track
+	 * in order to check for any join it's going to participate in whether parametrization
+	 * is possible.
+	 */
+	if (rte->rtekind == RTE_FUNCTION) {
+		FuncExpr *funcExpr = (FuncExpr *) rte->funcexpr;
+
+		if (funcExpr->args != NULL) {
+			rel->func_arg_relids = pull_varnos((Node *) funcExpr->args);
+		}
+	}
+
 	/* Save the finished struct in the query's simple_rel_array */
 	root->simple_rel_array[relid] = rel;
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 97ab9d5..6178b17 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -557,25 +557,6 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 	assign_expr_collations(pstate, funcexpr);
 
 	/*
-	 * The function parameters cannot make use of any variables from other
-	 * FROM items.	(Compare to transformRangeSubselect(); the coding is
-	 * different though because we didn't parse as a sub-select with its own
-	 * level of namespace.)
-	 *
-	 * XXX this will need further work to support SQL99's LATERAL() feature,
-	 * wherein such references would indeed be legal.
-	 */
-	if (pstate->p_relnamespace || pstate->p_varnamespace)
-	{
-		if (contain_vars_of_level(funcexpr, 0))
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-					 errmsg("function expression in FROM cannot refer to other relations of same query level"),
-					 parser_errposition(pstate,
-										locate_var_of_level(funcexpr, 0))));
-	}
-
-	/*
 	 * Disallow aggregate functions in the expression.	(No reason to postpone
 	 * this check until parseCheckAggregates.)
 	 */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index e1d5fc0..c3f3394 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -292,6 +292,10 @@ typedef struct PlannerInfo
  *
  *		relids - Set of base-relation identifiers; it is a base relation
  *				if there is just one, a join relation if more than one
+ *		func_arg_relids - union of relations that all functions within the current relation
+ *			reference via function arglists. Unlike clause parameters, this set is only
+ *			determined by the functions contained in 'relids'. That's why it's not stored
+ *			in 'ppilist' for each specific path.
  *		rows - estimated number of tuples in the relation after restriction
  *			   clauses have been applied (ie, output rows of a plan for it)
  *		width - avg. number of bytes per tuple in the relation after the
@@ -394,6 +398,9 @@ typedef struct RelOptInfo
 	/* all relations included in this RelOptInfo */
 	Relids		relids;			/* set of base relids (rangetable indexes) */
 
+	 /* union of  relids used as parameters by all RTE functions that this relation contains */
+	Relids	func_arg_relids;
+
 	/* size estimates generated by planner */
 	double		rows;			/* estimated number of result tuples */
 	int			width;			/* estimated avg width of result tuples */






