Hi

I would to introduce new flag for routines - PRIVATE. Routines with this
flag can be called only from other routines assigned with same schema.
Because these routines are not available for top queries, we can hide these
functions from some outputs like \df ..

Example:

CREATE SCHEMA s1;
CREATE SCHEMA s2;

CREATE OR REPLACE FUNCTION s1.nested()
RETURNS int AS $$
BEGIN
  RETURN random()*1000;
END;
$$ PRIVATE;

SELECT s1.nested(); -- fails - it is top query

CREATE OR REPLACE FUNCTION s1.fx()
RETURNS int AS $$
BEGIN
  RETURN s1.nested();
END;
$$ LANGUAGE plpgsql;

SELECT s1.fx(); -- will work

CREATE OR REPLACE FUNCTION s2.fx()
RETURNS int AS $$
BEGIN
  RETURN s1.nested();
END;
$$ LANGUAGE plpgsql;

SELECT s2.fx(); -- fails - it call private function from other schema.

This proposal is simple, and strong enough to separate functions that can
be directly callable and auxiliary functions, that can be called from other
functions.

I wrote PoC implementation, and it is not hard, and it should not to impact
performance. I introduced query_owner_nspid into query environment. When
any functions has flag private, then query_owner_nspid should be same like
function namespace id.

Comments, notes?

Regards

Pavel
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 246776093e..be634b87e7 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -625,6 +625,7 @@ AggregateCreate(const char *aggName,
 							 PROVOLATILE_IMMUTABLE, /* volatility (not needed
 													 * for agg) */
 							 proparallel,
+							 false,				/* isPrivate, now false */
 							 parameterTypes,	/* paramTypes */
 							 allParameterTypes, /* allParamTypes */
 							 parameterModes,	/* parameterModes */
@@ -798,6 +799,8 @@ lookup_agg_function(List *fnName,
 	FuncDetailCode fdresult;
 	AclResult	aclresult;
 	int			i;
+	bool		is_private;
+	Oid			nspid;
 
 	/*
 	 * func_get_detail looks up the function in the catalogs, does
@@ -810,7 +813,8 @@ lookup_agg_function(List *fnName,
 							   nargs, input_types, false, false,
 							   &fnOid, rettype, &retset,
 							   &nvargs, &vatype,
-							   &true_oid_array, NULL);
+							   &true_oid_array, NULL,
+							   &is_private, &nspid);
 
 	/* only valid case is a normal function not returning a set */
 	if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 9b4015d0d4..4b11485442 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -79,6 +79,7 @@ ProcedureCreate(const char *procedureName,
 				bool isStrict,
 				char volatility,
 				char parallel,
+				bool isPrivate,
 				oidvector *parameterTypes,
 				Datum allParameterTypes,
 				Datum parameterModes,
@@ -329,6 +330,7 @@ ProcedureCreate(const char *procedureName,
 	values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));
 	values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
 	values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
+	values[Anum_pg_proc_proisprivate - 1] = BoolGetDatum(isPrivate);
 	if (allParameterTypes != PointerGetDatum(NULL))
 		values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
 	else
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 68109bfda0..51907dd678 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -479,7 +479,8 @@ compute_common_attribute(ParseState *pstate,
 						 List **set_items,
 						 DefElem **cost_item,
 						 DefElem **rows_item,
-						 DefElem **parallel_item)
+						 DefElem **parallel_item,
+						 DefElem **scope_item)
 {
 	if (strcmp(defel->defname, "volatility") == 0)
 	{
@@ -546,6 +547,13 @@ compute_common_attribute(ParseState *pstate,
 
 		*parallel_item = defel;
 	}
+	else if (strcmp(defel->defname, "scope") == 0)
+	{
+		if (*scope_item)
+			goto duplicate_error;
+
+		*scope_item = defel;
+	}
 	else
 		return false;
 
@@ -655,7 +663,8 @@ compute_function_attributes(ParseState *pstate,
 							ArrayType **proconfig,
 							float4 *procost,
 							float4 *prorows,
-							char *parallel_p)
+							char *parallel_p,
+							bool *isPrivate)
 {
 	ListCell   *option;
 	DefElem    *as_item = NULL;
@@ -670,6 +679,7 @@ compute_function_attributes(ParseState *pstate,
 	DefElem    *cost_item = NULL;
 	DefElem    *rows_item = NULL;
 	DefElem    *parallel_item = NULL;
+	DefElem    *scope_item = NULL;
 
 	foreach(option, options)
 	{
@@ -726,7 +736,8 @@ compute_function_attributes(ParseState *pstate,
 										  &set_items,
 										  &cost_item,
 										  &rows_item,
-										  &parallel_item))
+										  &parallel_item,
+										  &scope_item))
 		{
 			/* recognized common option */
 			continue;
@@ -790,6 +801,11 @@ compute_function_attributes(ParseState *pstate,
 	}
 	if (parallel_item)
 		*parallel_p = interpret_func_parallel(parallel_item);
+	if (scope_item)
+	{
+		if (strcmp(strVal(scope_item->arg), "private") == 0)
+			*isPrivate = true;
+	}
 }
 
 
@@ -889,6 +905,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 				isStrict,
 				security,
 				isLeakProof;
+	bool		isPrivate;
 	char		volatility;
 	ArrayType  *proconfig;
 	float4		procost;
@@ -918,6 +935,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 	procost = -1;				/* indicates not set */
 	prorows = -1;				/* indicates not set */
 	parallel = PROPARALLEL_UNSAFE;
+	isPrivate = false;
 
 	/* Extract non-default attributes from stmt->options list */
 	compute_function_attributes(pstate,
@@ -926,7 +944,8 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 								&as_clause, &language, &transformDefElem,
 								&isWindowFunc, &volatility,
 								&isStrict, &security, &isLeakProof,
-								&proconfig, &procost, &prorows, &parallel);
+								&proconfig, &procost, &prorows, &parallel,
+								&isPrivate);
 
 	/* Look up the language and validate permissions */
 	languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
@@ -1106,6 +1125,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 						   isStrict,
 						   volatility,
 						   parallel,
+						   isPrivate,
 						   parameterTypes,
 						   PointerGetDatum(allParameterTypes),
 						   PointerGetDatum(parameterModes),
@@ -1188,6 +1208,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 	DefElem    *cost_item = NULL;
 	DefElem    *rows_item = NULL;
 	DefElem    *parallel_item = NULL;
+	DefElem    *scope_item = NULL;
 	ObjectAddress address;
 
 	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
@@ -1228,7 +1249,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 									 &set_items,
 									 &cost_item,
 									 &rows_item,
-									 &parallel_item) == false)
+									 &parallel_item,
+									 &scope_item) == false)
 			elog(ERROR, "option \"%s\" not recognized", defel->defname);
 	}
 
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index c900ad9431..35dd0a619a 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -134,6 +134,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 									  false,	/* isStrict */
 									  PROVOLATILE_VOLATILE,
 									  PROPARALLEL_UNSAFE,
+									  false,	/* isPrivate */
 									  buildoidvector(funcargtypes, 0),
 									  PointerGetDatum(NULL),
 									  PointerGetDatum(NULL),
@@ -173,6 +174,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										  true, /* isStrict */
 										  PROVOLATILE_VOLATILE,
 										  PROPARALLEL_UNSAFE,
+										  false,	/* isPrivate */
 										  buildoidvector(funcargtypes, 1),
 										  PointerGetDatum(NULL),
 										  PointerGetDatum(NULL),
@@ -215,6 +217,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										  true, /* isStrict */
 										  PROVOLATILE_VOLATILE,
 										  PROPARALLEL_UNSAFE,
+										  false,	/* isPrivate */
 										  buildoidvector(funcargtypes, 1),
 										  PointerGetDatum(NULL),
 										  PointerGetDatum(NULL),
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 175ecc8b48..9525b766a1 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1675,6 +1675,7 @@ makeRangeConstructors(const char *name, Oid namespace,
 								 false, /* isStrict */
 								 PROVOLATILE_IMMUTABLE, /* volatility */
 								 PROPARALLEL_SAFE,	/* parallel safety */
+								 false, /* isPrivate */
 								 constructorArgTypesVector, /* parameterTypes */
 								 PointerGetDatum(NULL), /* allParameterTypes */
 								 PointerGetDatum(NULL), /* parameterModes */
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd71d7..54ba40d2d0 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -153,7 +153,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
  * and instead we may have a ParamListInfo describing PARAM_EXTERN Params.
  */
 ExprState *
-ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
+ExecInitExprWithParams(Expr *node, ParamListInfo ext_params, Oid query_owner_nspid)
 {
 	ExprState  *state;
 	ExprEvalStep scratch = {0};
@@ -167,6 +167,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
 	state->expr = node;
 	state->parent = NULL;
 	state->ext_params = ext_params;
+	state->query_owner_nspid = query_owner_nspid;
 
 	/* Insert EEOP_*_FETCHSOME steps as needed */
 	ExecInitExprSlots(state, (Node *) node);
@@ -879,6 +880,25 @@ ExecInitExprRec(Expr *node, ExprState *state,
 			{
 				FuncExpr   *func = (FuncExpr *) node;
 
+				/* check scope of function */
+				if (func->funcprivate)
+				{
+					Oid		query_owner_nspid = state->query_owner_nspid;
+
+					if (OidIsValid(state->query_owner_nspid))
+						query_owner_nspid = state->query_owner_nspid;
+					else if (state->parent && state->parent->state)
+						query_owner_nspid =
+							  state->parent->state->es_query_owner_nspid;
+
+					if (!OidIsValid(query_owner_nspid))
+						elog(ERROR, "private function can be called only from routine");
+					if (query_owner_nspid != func->funcnspid)
+						elog(ERROR, "private function of \"%s\" used from function from \"%s\"",
+							 get_namespace_name(func->funcnspid),
+							 get_namespace_name(query_owner_nspid));
+				}
+
 				ExecInitFunc(&scratch, node,
 							 func->args, func->funcid, func->inputcollid,
 							 state);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index c583e020a0..00ca74a804 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -824,6 +824,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	estate->es_range_table = rangeTable;
 	estate->es_plannedstmt = plannedstmt;
 
+	/* ToDo: Better to store query_owner_nspid to QueryDesc */
+	estate->es_query_owner_nspid = plannedstmt->query_owner_nspid;
+
 	/*
 	 * initialize result relation stuff, and open/lock the result rels.
 	 *
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index ee0f07a81e..ec88d8160e 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -1102,6 +1102,7 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
 	PlannedStmt *pstmt;
 	ParamListInfo paramLI;
 	char	   *queryString;
+	Oid			query_owner_nspid;
 
 	/* Get the query string from shared memory */
 	queryString = shm_toc_lookup(toc, PARALLEL_KEY_QUERY_TEXT, false);
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5b3eaec80b..8b7d52bf57 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -159,6 +159,8 @@ CreateExecutorState(void)
 
 	estate->es_use_parallel_mode = false;
 
+	estate->es_query_owner_nspid = InvalidOid;
+
 	estate->es_jit_flags = 0;
 	estate->es_jit = NULL;
 
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 5756365c8f..6c01be90b9 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -2149,6 +2149,9 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 			_SPI_current->lastoid = InvalidOid;
 			_SPI_current->tuptable = NULL;
 
+			/* inject assigned query_owner_nspid */
+			stmt->query_owner_nspid = plan->query_owner_nspid;
+
 			if (stmt->utilityStmt)
 			{
 				if (IsA(stmt->utilityStmt, CopyStmt))
@@ -2200,6 +2203,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 				else
 					snap = InvalidSnapshot;
 
+				/*
+				 * ToDo: better place to inject query owner nspid, but
+				 * we should to implement passing query owner nspid over
+				 * parallel workers.
+				 */
 				qdesc = CreateQueryDesc(stmt,
 										plansource->query_string,
 										snap, crosscheck_snapshot,
@@ -2697,6 +2705,8 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
 	/* For safety, unlink the CachedPlanSources from the temporary plan */
 	plan->plancache_list = NIL;
 
+	newplan->query_owner_nspid = plan->query_owner_nspid;
+
 	return newplan;
 }
 
@@ -2767,9 +2777,21 @@ _SPI_save_plan(SPIPlanPtr plan)
 		SaveCachedPlan(plansource);
 	}
 
+	newplan->query_owner_nspid = plan->query_owner_nspid;
+
 	return newplan;
 }
 
+/*
+ * Assign query owner namespace to plan. The query owner nspid
+ * is used for implementation of schema private functions.
+ */
+void
+assign_query_owner_nspid(SPIPlanPtr plan, Oid query_owner_nspid)
+{
+	plan->query_owner_nspid = query_owner_nspid;
+}
+
 /*
  * Internal lookup of ephemeral named relation by name.
  */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220cf65..b52f287c35 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -102,6 +102,7 @@ _copyPlannedStmt(const PlannedStmt *from)
 	COPY_NODE_FIELD(utilityStmt);
 	COPY_LOCATION_FIELD(stmt_location);
 	COPY_LOCATION_FIELD(stmt_len);
+	COPY_SCALAR_FIELD(query_owner_nspid);
 
 	return newnode;
 }
@@ -1524,6 +1525,8 @@ _copyFuncExpr(const FuncExpr *from)
 	COPY_SCALAR_FIELD(funccollid);
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
+	COPY_SCALAR_FIELD(funcprivate);
+	COPY_SCALAR_FIELD(funcnspid);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 378f2facb8..144034760f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -289,6 +289,8 @@ _equalFuncExpr(const FuncExpr *a, const FuncExpr *b)
 	COMPARE_SCALAR_FIELD(funccollid);
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
+	COMPARE_SCALAR_FIELD(funcprivate);
+	COMPARE_SCALAR_FIELD(funcnspid);
 	COMPARE_LOCATION_FIELD(location);
 
 	return true;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 1bd2599c2c..2e78dd8f18 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -526,6 +526,8 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args,
 	funcexpr->funcresulttype = rettype;
 	funcexpr->funcretset = false;	/* only allowed case here */
 	funcexpr->funcvariadic = false; /* only allowed case here */
+	funcexpr->funcprivate = false;	/* only allowed case here */
+	funcexpr->funcnspid = InvalidOid;
 	funcexpr->funcformat = fformat;
 	funcexpr->funccollid = funccollid;
 	funcexpr->inputcollid = inputcollid;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f474d2..6a8cb3af23 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -287,6 +287,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
 	WRITE_NODE_FIELD(utilityStmt);
 	WRITE_LOCATION_FIELD(stmt_location);
 	WRITE_LOCATION_FIELD(stmt_len);
+	WRITE_OID_FIELD(query_owner_nspid);
 }
 
 /*
@@ -1278,6 +1279,8 @@ _outFuncExpr(StringInfo str, const FuncExpr *node)
 	WRITE_OID_FIELD(funccollid);
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
+	WRITE_BOOL_FIELD(funcprivate);
+	WRITE_OID_FIELD(funcnspid);
 	WRITE_LOCATION_FIELD(location);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524223..7cde1caf47 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -680,6 +680,8 @@ _readFuncExpr(void)
 	READ_OID_FIELD(funccollid);
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
+	READ_BOOL_FIELD(funcprivate);
+	READ_OID_FIELD(funcnspid);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -1494,6 +1496,7 @@ _readPlannedStmt(void)
 	READ_NODE_FIELD(utilityStmt);
 	READ_LOCATION_FIELD(stmt_location);
 	READ_LOCATION_FIELD(stmt_len);
+	READ_OID_FIELD(query_owner_nspid);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 01335db511..2ae754fdb5 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -214,6 +214,7 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars,
 
 			if (bms_is_subset(where_needed, rel->relids))
 				continue;
+
 			Assert(attno >= rel->min_attr && attno <= rel->max_attr);
 			attno -= rel->min_attr;
 			if (rel->attr_needed[attno] == NULL)
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 96bf0601a8..750d4ea430 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -532,6 +532,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	result->stmt_location = parse->stmt_location;
 	result->stmt_len = parse->stmt_len;
 
+	result->query_owner_nspid = InvalidOid;
+
 	result->jitFlags = PGJIT_NONE;
 	if (jit_enabled && jit_above_cost >= 0 &&
 		top_plan->total_cost > jit_above_cost)
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a04ad6e99e..c9a6bd16b3 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -129,6 +129,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 funcprivate, Oid funcnspid,
 				  eval_const_expressions_context *context);
 static List *reorder_function_arguments(List *args, HeapTuple func_tuple);
 static List *add_function_defaults(List *args, HeapTuple func_tuple);
@@ -138,11 +139,13 @@ static void recheck_cast_function_args(List *args, Oid result_type,
 static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List *args,
 				  bool funcvariadic,
+				  bool funcprivate, Oid funcnspid,
 				  HeapTuple func_tuple,
 				  eval_const_expressions_context *context);
 static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
 				Oid input_collid, List *args,
 				bool funcvariadic,
+				bool funcprivate, Oid funcnspid,
 				HeapTuple func_tuple,
 				eval_const_expressions_context *context);
 static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
@@ -2686,6 +2689,8 @@ eval_const_expressions_mutator(Node *node,
 										   expr->funcvariadic,
 										   true,
 										   true,
+										   expr->funcprivate,
+										   expr->funcnspid,
 										   context);
 				if (simple)		/* successfully simplified it */
 					return (Node *) simple;
@@ -2705,6 +2710,8 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->funccollid = expr->funccollid;
 				newexpr->inputcollid = expr->inputcollid;
 				newexpr->args = args;
+				newexpr->funcprivate = expr->funcprivate;
+				newexpr->funcnspid = expr->funcnspid;
 				newexpr->location = expr->location;
 				return (Node *) newexpr;
 			}
@@ -2733,6 +2740,8 @@ eval_const_expressions_mutator(Node *node,
 										   false,
 										   true,
 										   true,
+										   false,
+										   InvalidOid,
 										   context);
 				if (simple)		/* successfully simplified it */
 					return (Node *) simple;
@@ -2837,6 +2846,8 @@ eval_const_expressions_mutator(Node *node,
 											   false,
 											   false,
 											   false,
+											   false,
+											   InvalidOid,
 											   context);
 					if (simple) /* successfully simplified it */
 					{
@@ -3059,6 +3070,8 @@ eval_const_expressions_mutator(Node *node,
 										   false,
 										   true,
 										   true,
+										   false,
+										   InvalidOid,
 										   context);
 				if (simple)		/* successfully simplified output fn */
 				{
@@ -3091,6 +3104,8 @@ eval_const_expressions_mutator(Node *node,
 											   false,
 											   false,
 											   true,
+											   false,
+											   InvalidOid,
 											   context);
 					if (simple) /* successfully simplified input fn */
 						return (Node *) simple;
@@ -4023,6 +4038,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 funcprivate, Oid funcnspid,
 				  eval_const_expressions_context *context)
 {
 	List	   *args = *args_p;
@@ -4068,6 +4084,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,
+								funcprivate, funcnspid,
 								func_tuple, context);
 
 	if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
@@ -4088,6 +4105,8 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 		fexpr.funccollid = result_collid;
 		fexpr.inputcollid = input_collid;
 		fexpr.args = args;
+		fexpr.funcprivate = funcprivate;
+		fexpr.funcnspid = funcnspid;
 		fexpr.location = -1;
 
 		newexpr = (Expr *)
@@ -4098,6 +4117,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
 	if (!newexpr && allow_non_const)
 		newexpr = inline_function(funcid, result_type, result_collid,
 								  input_collid, args, funcvariadic,
+								  funcprivate, funcnspid,
 								  func_tuple, context);
 
 	ReleaseSysCache(func_tuple);
@@ -4337,6 +4357,7 @@ static Expr *
 evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List *args,
 				  bool funcvariadic,
+				  bool funcprivate, Oid funcnspid,
 				  HeapTuple func_tuple,
 				  eval_const_expressions_context *context)
 {
@@ -4423,6 +4444,8 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 	newexpr->funccollid = result_collid;	/* doesn't matter */
 	newexpr->inputcollid = input_collid;
 	newexpr->args = args;
+	newexpr->funcprivate = funcprivate;
+	newexpr->funcnspid = funcnspid;
 	newexpr->location = -1;
 
 	return evaluate_expr((Expr *) newexpr, result_type, result_typmod,
@@ -4464,6 +4487,7 @@ static Expr *
 inline_function(Oid funcid, Oid result_type, Oid result_collid,
 				Oid input_collid, List *args,
 				bool funcvariadic,
+				bool funcprivate, Oid funcnspid,
 				HeapTuple func_tuple,
 				eval_const_expressions_context *context)
 {
@@ -4556,6 +4580,8 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 	fexpr->funccollid = result_collid;	/* doesn't matter */
 	fexpr->inputcollid = input_collid;
 	fexpr->args = args;
+	fexpr->funcprivate = funcprivate;
+	fexpr->funcnspid = funcnspid;
 	fexpr->location = -1;
 
 	pinfo = prepare_sql_fn_parse_info(func_tuple,
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 87f5e95827..13a87b33a4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -664,7 +664,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
 	POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
-	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
+	PRIOR PRIVATE PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
 
 	QUOTE
 
@@ -7936,6 +7936,11 @@ common_func_opt_item:
 				{
 					$$ = makeDefElem("parallel", (Node *)makeString($2), @1);
 				}
+			| PRIVATE
+				{
+					/* This function can be called only from routines */
+					$$ = makeDefElem("scope", (Node *)makeString("private"), @1);
+				}
 		;
 
 createfunc_opt_item:
@@ -15181,6 +15186,7 @@ unreserved_keyword:
 			| PREPARED
 			| PRESERVE
 			| PRIOR
+			| PRIVATE
 			| PRIVILEGES
 			| PROCEDURAL
 			| PROCEDURE
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 44257154b8..83aa22b053 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -102,6 +102,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 	bool		retset;
 	int			nvargs;
 	Oid			vatype;
+	bool		funcprivate;
+	Oid			funcnspid;
 	FuncDetailCode fdresult;
 	char		aggkind = 0;
 	ParseCallbackState pcbstate;
@@ -259,7 +261,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 							   !func_variadic, true,
 							   &funcid, &rettype, &retset,
 							   &nvargs, &vatype,
-							   &declared_arg_types, &argdefaults);
+							   &declared_arg_types, &argdefaults,
+							   &funcprivate, &funcnspid);
 
 	cancel_parser_errposition_callback(&pcbstate);
 
@@ -739,6 +742,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		funcexpr->funcformat = COERCE_EXPLICIT_CALL;
 		/* funccollid and inputcollid will be set by parse_collate.c */
 		funcexpr->args = fargs;
+		funcexpr->funcprivate = funcprivate;
+		funcexpr->funcnspid = funcnspid;
 		funcexpr->location = location;
 
 		retval = (Node *) funcexpr;
@@ -1386,7 +1391,9 @@ func_get_detail(List *funcname,
 				int *nvargs,	/* return value */
 				Oid *vatype,	/* return value */
 				Oid **true_typeids, /* return value */
-				List **argdefaults) /* optional return value */
+				List **argdefaults, /* optional return value */
+				bool *isprivate,  /* return value */
+				Oid *namespace)	/* return value */
 {
 	FuncCandidateList raw_candidates;
 	FuncCandidateList best_candidate;
@@ -1610,6 +1617,10 @@ func_get_detail(List *funcname,
 		*rettype = pform->prorettype;
 		*retset = pform->proretset;
 		*vatype = pform->provariadic;
+
+		*isprivate = pform->proisprivate;
+		*namespace = pform->pronamespace;
+
 		/* fetch default args if caller wants 'em */
 		if (argdefaults && best_candidate->ndargs > 0)
 		{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 03e9a28a63..f51693d7af 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2588,6 +2588,9 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 	if (proc->proleakproof)
 		appendStringInfoString(&buf, " LEAKPROOF");
 
+	if (proc->proisprivate)
+		appendStringInfoString(&buf, " PRIVATE");
+
 	/* This code for the default cost and rows should match functioncmds.c */
 	if (proc->prolang == INTERNALlanguageId ||
 		proc->prolang == ClanguageId)
@@ -10765,6 +10768,8 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
 	Oid			p_vatype;
 	Oid		   *p_true_typeids;
 	bool		force_qualify = false;
+	bool		p_isprivate;
+	Oid			p_nspid;
 
 	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
 	if (!HeapTupleIsValid(proctup))
@@ -10818,7 +10823,8 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
 								   !use_variadic, true,
 								   &p_funcid, &p_rettype,
 								   &p_retset, &p_nvargs, &p_vatype,
-								   &p_true_typeids, NULL);
+								   &p_true_typeids, NULL,
+								   &p_isprivate, &p_nspid);
 	else
 	{
 		p_result = FUNCDETAIL_NOTFOUND;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9baf7b2fde..63752d8fca 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -11532,6 +11532,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	char	   *procost;
 	char	   *prorows;
 	char	   *proparallel;
+	char	   *proisprivate;
 	char	   *lanname;
 	char	   *rettypename;
 	int			nallargs;
@@ -11553,6 +11554,26 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	asPart = createPQExpBuffer();
 
 	/* Fetch function-specific details */
+	if (fout->remoteVersion > 110000)
+	{
+		/*
+		 * proisprivate was added in 12
+		 */
+		appendPQExpBuffer(query,
+						  "SELECT proretset, prosrc, probin, "
+						  "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
+						  "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
+						  "pg_catalog.pg_get_function_result(oid) AS funcresult, "
+						  "array_to_string(protrftypes, ' ') AS protrftypes, "
+						  "prokind, provolatile, proisstrict, prosecdef, "
+						  "proleakproof, proconfig, procost, prorows, "
+						  "proparallel, "
+						  "proisprivate, "
+						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+						  "FROM pg_catalog.pg_proc "
+						  "WHERE oid = '%u'::pg_catalog.oid",
+						  finfo->dobj.catId.oid);
+	}
 	if (fout->remoteVersion >= 110000)
 	{
 		/*
@@ -11731,6 +11752,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	else
 		proparallel = NULL;
 
+	if (PQfnumber(res, "proisprivate") != -1)
+		proisprivate = PQgetvalue(res, 0, PQfnumber(res, "proisprivate"));
+	else
+		proisprivate = NULL;
+
 	lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
 
 	/*
@@ -11914,6 +11940,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	if (proleakproof[0] == 't')
 		appendPQExpBufferStr(q, " LEAKPROOF");
 
+
+
 	/*
 	 * COST and ROWS are emitted only if present and not default, so as not to
 	 * break backwards-compatibility of the dump without need.  Keep this code
@@ -11949,6 +11977,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  finfo->dobj.name);
 	}
 
+	if (proisprivate && proisprivate[0] == 't')
+		appendPQExpBuffer(q, " PRIVATE");
+
 	for (i = 0; i < nconfigitems; i++)
 	{
 		/* we feel free to scribble on configitems[] here */
diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat
index 9fffdef379..48ab7b2ec8 100644
--- a/src/include/catalog/pg_class.dat
+++ b/src/include/catalog/pg_class.dat
@@ -47,7 +47,7 @@
   reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0',
   reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0',
   reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
-  relpersistence => 'p', relkind => 'r', relnatts => '28', relchecks => '0',
+  relpersistence => 'p', relkind => 'r', relnatts => '29', relchecks => '0',
   relhasoids => 't', relhasrules => 'f', relhastriggers => 'f',
   relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f',
   relispopulated => 't', relreplident => 'n', relispartition => 'f',
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a34b2596fa..0b929a7365 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -75,6 +75,9 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce
 	/* see PROPARALLEL_ categories below */
 	char		proparallel BKI_DEFAULT(s);
 
+	/* is it a private function? */
+	bool		proisprivate BKI_DEFAULT(f);
+
 	/* number of arguments */
 	/* Note: need not be given in pg_proc.dat; genbki.pl will compute it */
 	int16		pronargs;
@@ -192,6 +195,7 @@ extern ObjectAddress ProcedureCreate(const char *procedureName,
 				bool isStrict,
 				char volatility,
 				char parallel,
+				bool isPrivate,
 				oidvector *parameterTypes,
 				Datum allParameterTypes,
 				Datum parameterModes,
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index 10e9ded246..472ac5dce0 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -42,6 +42,7 @@ typedef struct QueryDesc
 	ParamListInfo params;		/* param values being passed in */
 	QueryEnvironment *queryEnv; /* query environment passed in */
 	int			instrument_options; /* OR of InstrumentOption flags */
+	Oid			query_owner_nspid; /* the query is evaluated from object assigned to schema */
 
 	/* These fields are set by ExecutorStart */
 	TupleDesc	tupDesc;		/* descriptor for result tuples */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index f82b51667f..ce1f5349d7 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -242,7 +242,7 @@ ExecProcNode(PlanState *node)
  * prototypes from functions in execExpr.c
  */
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
-extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
+extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params, Oid query_owner_nspid);
 extern ExprState *ExecInitQual(List *qual, PlanState *parent);
 extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
 extern List *ExecInitExprList(List *nodes, PlanState *parent);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 143a89a16c..df9037aa51 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -167,4 +167,6 @@ extern void SPICleanup(void);
 extern void AtEOXact_SPI(bool isCommit);
 extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
 
+extern void assign_query_owner_nspid(SPIPlanPtr plan, Oid query_owner_nspid);
+
 #endif							/* SPI_H */
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 401fd998f7..d6d88ab5a5 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -96,6 +96,7 @@ typedef struct _SPI_plan
 	Oid		   *argtypes;		/* Argument types (NULL if nargs is 0) */
 	ParserSetupHook parserSetup;	/* alternative parameter spec method */
 	void	   *parserSetupArg;
+	Oid			query_owner_nspid;
 } _SPI_plan;
 
 #endif							/* SPI_PRIV_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 41fa2052a2..169bea82af 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -108,6 +108,8 @@ typedef struct ExprState
 
 	Datum	   *innermost_domainval;
 	bool	   *innermost_domainnull;
+
+	Oid			query_owner_nspid;
 } ExprState;
 
 
@@ -253,6 +255,9 @@ typedef struct ExprContext
 	/* Link to containing EState (NULL if a standalone ExprContext) */
 	struct EState *ecxt_estate;
 
+	/* Oid of namespace of object owning this query */
+	Oid			ecxt_query_owner_nspid;
+
 	/* Functions to call back when ExprContext is shut down or rescanned */
 	ExprContext_CB *ecxt_callbacks;
 } ExprContext;
@@ -564,6 +569,8 @@ typedef struct EState
 	/* The per-query shared memory area to use for parallel execution. */
 	struct dsa_area *es_query_dsa;
 
+	Oid			es_query_owner_nspid;
+
 	/*
 	 * JIT information. es_jit_flags indicates whether JIT should be performed
 	 * and with which options.  es_jit is created on-demand when JITing is
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd03a..94e29a857e 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -99,6 +99,8 @@ typedef struct PlannedStmt
 	/* statement location in source string (copied from Query) */
 	int			stmt_location;	/* start location, or -1 if unknown */
 	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
+
+	Oid			query_owner_nspid;
 } PlannedStmt;
 
 /* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d75af..0ddcd59e88 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -456,6 +456,8 @@ 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		funcprivate;	/* true if function is private */
+	Oid			funcnspid;		/* OID of function namespace */
 } FuncExpr;
 
 /*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db40147b..576f211adf 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -311,6 +311,7 @@ PG_KEYWORD("prepared", PREPARED, UNRESERVED_KEYWORD)
 PG_KEYWORD("preserve", PRESERVE, UNRESERVED_KEYWORD)
 PG_KEYWORD("primary", PRIMARY, RESERVED_KEYWORD)
 PG_KEYWORD("prior", PRIOR, UNRESERVED_KEYWORD)
+PG_KEYWORD("private", PRIVATE, UNRESERVED_KEYWORD)
 PG_KEYWORD("privileges", PRIVILEGES, UNRESERVED_KEYWORD)
 PG_KEYWORD("procedural", PROCEDURAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("procedure", PROCEDURE, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 11f9046e38..ae6cdc8f7a 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -41,7 +41,8 @@ extern FuncDetailCode func_get_detail(List *funcname,
 				bool expand_variadic, bool expand_defaults,
 				Oid *funcid, Oid *rettype,
 				bool *retset, int *nvargs, Oid *vatype,
-				Oid **true_typeids, List **argdefaults);
+				Oid **true_typeids, List **argdefaults,
+				bool *isprivate, Oid *namespace);
 
 extern int func_match_argtypes(int nargs,
 					Oid *input_typeids,
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 721234d6d2..a35c61ec58 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -367,6 +367,7 @@ do_compile(FunctionCallInfo fcinfo,
 		function->fn_is_trigger = PLPGSQL_NOT_TRIGGER;
 
 	function->fn_prokind = procStruct->prokind;
+	function->fn_namespace = procStruct->pronamespace;
 
 	/*
 	 * Initialize the compiler, particularly the namespace stack.  The
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 380d1de8f4..22efcbe80c 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3989,6 +3989,9 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
 	if (plan == NULL)
 		elog(ERROR, "SPI_prepare_params failed for \"%s\": %s",
 			 expr->query, SPI_result_code_string(SPI_result));
+
+	assign_query_owner_nspid(plan, estate->func->fn_namespace);
+
 	if (keepplan)
 		SPI_keepplan(plan);
 	expr->plan = plan;
@@ -6060,6 +6063,9 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 
 	econtext->ecxt_param_list_info = setup_param_list(estate, expr);
 
+	/* Setup query owner nspid - used for check of usage of private functions */
+	econtext->ecxt_query_owner_nspid = estate->func->fn_namespace;
+
 	/*
 	 * Prepare the expression for execution, if it's not been done already in
 	 * the current transaction.  (This will be forced to happen if we called
@@ -6070,7 +6076,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 		oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt);
 		expr->expr_simple_state =
 			ExecInitExprWithParams(expr->expr_simple_expr,
-								   econtext->ecxt_param_list_info);
+								   econtext->ecxt_param_list_info,
+								   estate->func->fn_namespace);
 		expr->expr_simple_in_use = false;
 		expr->expr_simple_lxid = curlxid;
 		MemoryContextSwitchTo(oldcontext);
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 4a4c7cbd36..a141a7e217 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -928,6 +928,7 @@ typedef struct PLpgSQL_function
 {
 	char	   *fn_signature;
 	Oid			fn_oid;
+	Oid			fn_namespace;
 	TransactionId fn_xmin;
 	ItemPointerData fn_tid;
 	PLpgSQL_trigtype fn_is_trigger;

Reply via email to