 contrib/sepgsql/expected/label.out   | 17 +++++++++
 contrib/sepgsql/expected/misc.out    | 67 ++++++++++++++++++++++++++++++++++++
 contrib/sepgsql/hooks.c              |  8 +++++
 contrib/sepgsql/label.c              | 23 +++++++++++--
 contrib/sepgsql/proc.c               | 26 ++++++++++++++
 contrib/sepgsql/sepgsql-regtest.te   | 17 ++-------
 contrib/sepgsql/sepgsql.h            |  1 +
 contrib/sepgsql/sql/label.sql        |  2 ++
 contrib/sepgsql/sql/misc.sql         | 25 ++++++++++++++
 doc/src/sgml/sepgsql.sgml            |  7 ++--
 src/backend/catalog/objectaccess.c   | 17 +++++++++
 src/backend/executor/execQual.c      |  3 ++
 src/backend/executor/nodeAgg.c       |  4 +++
 src/backend/executor/nodeWindowAgg.c |  4 +++
 src/backend/tcop/fastpath.c          |  2 ++
 src/include/catalog/objectaccess.h   | 14 ++++++++
 16 files changed, 218 insertions(+), 19 deletions(-)

diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
index 0a15f27..18bdf81 100644
--- a/contrib/sepgsql/expected/label.out
+++ b/contrib/sepgsql/expected/label.out
@@ -131,23 +131,40 @@ SELECT sepgsql_getcon();	-- confirm client privilege
  unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
 (1 row)
 
+SET sepgsql.debug_audit = true;
+SET client_min_messages = log;
 SELECT f1();			-- normal procedure
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function f1()"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function sepgsql_getcon()"
+CONTEXT:  SQL function "f1" statement 1
                          f1                          
 -----------------------------------------------------
  unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
 (1 row)
 
 SELECT f2();			-- trusted procedure
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f2()"
+LOG:  SELinux: allowed { entrypoint } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f2()"
+LOG:  SELinux: allowed { transition } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0 tclass=process
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function sepgsql_getcon()"
+CONTEXT:  SQL function "f2" statement 1
                          f2                          
 -----------------------------------------------------
  unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0
 (1 row)
 
 SELECT f3();			-- trusted procedure that raises an error
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f3()"
+LOG:  SELinux: allowed { entrypoint } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f3()"
+LOG:  SELinux: allowed { transition } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0 tclass=process
 ERROR:  an exception from f3()
 SELECT f4();			-- failed on domain transition
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0 tclass=db_procedure name="function f4()"
+LOG:  SELinux: allowed { entrypoint } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0 tclass=db_procedure name="function f4()"
+LOG:  SELinux: denied { transition } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:unconfined_r:sepgsql_regtest_nosuch_t:s0 tclass=process
 ERROR:  SELinux: security policy violation
 SELECT sepgsql_getcon();	-- client's label must be restored
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function sepgsql_getcon()"
                    sepgsql_getcon                    
 -----------------------------------------------------
  unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out
index 329852c..aaa5d25 100644
--- a/contrib/sepgsql/expected/misc.out
+++ b/contrib/sepgsql/expected/misc.out
@@ -3,3 +3,70 @@
 --
 LOAD '$libdir/sepgsql';		-- failed
 ERROR:  SELinux: LOAD is not permitted
+--
+-- Permissions to execute functions
+--
+CREATE TABLE t1 (x int, y text);
+INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x);
+SET sepgsql.debug_audit = on;
+SET client_min_messages = log;
+-- regular function and operators
+SELECT * FROM t1 WHERE x > 50 AND y like '%64%';
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table t1"
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x"
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column y"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int4gt(integer,integer)"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function textlike(text,text)"
+  x  |                y                 
+-----+----------------------------------
+  77 | 28dd2c7955ce926456240b2ff0100bde
+  89 | 7647966b7343c29048673252e490f736
+  90 | 8613985ec49eb8f757ae6439e879bb2a
+  91 | 54229abfcfa5649e7003b83dd4755294
+  99 | ac627ab1ccbdb62ec96e702f07f6425b
+ 100 | f899139df5e1059396431415e770c6dd
+(6 rows)
+
+-- aggregate function
+SELECT MIN(x), AVG(x) FROM t1;
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table t1"
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function avg(integer)"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int4_avg_accum(bigint[],integer)"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int8_avg(bigint[])"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function min(integer)"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int4smaller(integer,integer)"
+ min |         avg         
+-----+---------------------
+   1 | 50.5000000000000000
+(1 row)
+
+-- window function
+SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%';
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table t1"
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x"
+LOG:  SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column y"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function row_number()"
+LOG:  SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function textlike(text,text)"
+ row_number | x  |                y                 
+------------+----+----------------------------------
+          1 |  2 | c81e728d9d4c2f636f067f89cc14862c
+          2 | 17 | 70efdf2ec9b086079795c442636b55fb
+          3 | 22 | b6d767d2f8ed5d21a44b0e5886680cb9
+          4 | 27 | 02e74f10e0327ad868d138f2b4fdd6f0
+          5 | 33 | 182be0c5cdcd5072bb1864cdee4d3d6e
+          6 | 43 | 17e62166fc8586dfa4d1bc0e1742c08b
+          7 | 54 | a684eceee76fc522773286a895bc8436
+          8 | 73 | d2ddea18f00665ce8623e36bd4e3c7c5
+          9 | 76 | fbd7939d674997cdb4692d34de8633c4
+         10 | 89 | 7647966b7343c29048673252e490f736
+         11 | 90 | 8613985ec49eb8f757ae6439e879bb2a
+         12 | 94 | f4b9ec30ad9f68f89b29639786cb62ef
+(12 rows)
+
+RESET sepgsql.debug_audit;
+RESET client_min_messages;
+--
+-- Cleanup
+--
+DROP TABLE IF EXISTS t1 CASCADE;
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 0715aa8..2ad46a8 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -236,6 +236,14 @@ sepgsql_object_access(ObjectAccessType access,
 			}
 			break;
 
+		case OAT_FUNCTION_EXEC:
+			{
+				Assert(classId == ProcedureRelationId);
+				sepgsql_proc_execute(objectId);
+
+			}
+			break;
+
 		default:
 			elog(ERROR, "unexpected object access type: %d", (int) access);
 			break;
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index a5bdde3..6146399 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -303,7 +303,8 @@ sepgsql_needs_fmgr_hook(Oid functionId)
 	object.objectSubId = 0;
 	if (!sepgsql_avc_check_perms(&object,
 								 SEPG_CLASS_DB_PROCEDURE,
-								 SEPG_DB_PROCEDURE__EXECUTE,
+								 SEPG_DB_PROCEDURE__EXECUTE |
+								 SEPG_DB_PROCEDURE__ENTRYPOINT,
 								 SEPGSQL_AVC_NOAUDIT, false))
 		return true;
 
@@ -347,13 +348,31 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
 				 * process:transition permission between old and new label,
 				 * when user tries to switch security label of the client on
 				 * execution of trusted procedure.
+				 *
+				 * Also, db_procedure:entrypoint permission should be checked
+				 * whether this procedure can perform as an entrypoint of the
+				 * trusted procedure, or not.
+				 * Note that db_procedure:execute permission shall be checked
+				 * individually.
 				 */
 				if (stack->new_label)
+				{
+					ObjectAddress object;
+
+					object.classId = ProcedureRelationId;
+					object.objectId = flinfo->fn_oid;
+					object.objectSubId = 0;
+					sepgsql_avc_check_perms(&object,
+											SEPG_CLASS_DB_PROCEDURE,
+											SEPG_DB_PROCEDURE__ENTRYPOINT,
+											getObjectDescription(&object),
+											true);
+
 					sepgsql_avc_check_perms_label(stack->new_label,
 												  SEPG_CLASS_PROCESS,
 												  SEPG_PROCESS__TRANSITION,
 												  NULL, true);
-
+				}
 				*private = PointerGetDatum(stack);
 			}
 			Assert(!stack->old_label);
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index afc3755..0cd2c8c 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -301,3 +301,29 @@ sepgsql_proc_setattr(Oid functionId)
 	systable_endscan(sscan);
 	heap_close(rel, AccessShareLock);
 }
+
+/*
+ * sepgsql_proc_execute
+ *
+ * It checks privileges to execute the supplied function
+ */
+void
+sepgsql_proc_execute(Oid functionId)
+{
+	ObjectAddress object;
+	char	   *audit_name;
+
+	/*
+	 * check db_procedure:{execute} permission
+	 */
+	object.classId = ProcedureRelationId;
+	object.objectId = functionId;
+	object.objectSubId = 0;
+	audit_name = getObjectDescription(&object);
+	sepgsql_avc_check_perms(&object,
+							SEPG_CLASS_DB_PROCEDURE,
+							SEPG_DB_PROCEDURE__EXECUTE,
+							audit_name,
+							true);
+	pfree(audit_name);
+}
diff --git a/contrib/sepgsql/sepgsql-regtest.te b/contrib/sepgsql/sepgsql-regtest.te
index 790c4e8..37c0316 100644
--- a/contrib/sepgsql/sepgsql-regtest.te
+++ b/contrib/sepgsql/sepgsql-regtest.te
@@ -1,4 +1,4 @@
-policy_module(sepgsql-regtest, 1.05)
+policy_module(sepgsql-regtest, 1.06)
 
 gen_require(`
 	all_userspace_class_perms
@@ -176,18 +176,5 @@ optional_policy(`
 	gen_require(`
 		attribute sepgsql_client_type;
 	')
-	allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute };
-
-	# These rules intends sepgsql_regtest_user_t domain to translate
-	# sepgsql_regtest_dba_t on execution of procedures labeled as
-	# sepgsql_regtest_trusted_proc_exec_t.
-	#
-#	allow sepgsql_client_type sepgsql_regtest_trusted_proc_exec_t:db_procedure { getattr execute };
-
-	# These rules intends sepgsql_regtest_user_t domain to translate
-	# sepgsql_regtest_nosuch_t on execution of procedures labeled as
-	# sepgsql_nosuch_trusted_proc_exec_t, without permissions to
-	# translate to sepgsql_nosuch_trusted_proc_exec_t.
-	#
-#	allow sepgsql_client_type sepgsql_nosuch_trusted_proc_exec_t:db_procedure { getattr execute install };
+	allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute entrypoint };
 ')
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index f7448cd..56094a8 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -327,5 +327,6 @@ extern void sepgsql_proc_post_create(Oid functionId);
 extern void sepgsql_proc_drop(Oid functionId);
 extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
 extern void sepgsql_proc_setattr(Oid functionId);
+extern void sepgsql_proc_execute(Oid functionId);
 
 #endif   /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql
index 6201cd7..7a05c24 100644
--- a/contrib/sepgsql/sql/label.sql
+++ b/contrib/sepgsql/sql/label.sql
@@ -97,6 +97,8 @@ SECURITY LABEL ON COLUMN t2.b
 -- Tests for Trusted Procedures
 --
 -- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = log;
 SELECT f1();			-- normal procedure
 SELECT f2();			-- trusted procedure
 SELECT f3();			-- trusted procedure that raises an error
diff --git a/contrib/sepgsql/sql/misc.sql b/contrib/sepgsql/sql/misc.sql
index a46d8a6..c277711 100644
--- a/contrib/sepgsql/sql/misc.sql
+++ b/contrib/sepgsql/sql/misc.sql
@@ -3,3 +3,28 @@
 --
 
 LOAD '$libdir/sepgsql';		-- failed
+
+--
+-- Permissions to execute functions
+--
+CREATE TABLE t1 (x int, y text);
+INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x);
+
+SET sepgsql.debug_audit = on;
+SET client_min_messages = log;
+
+-- regular function and operators
+SELECT * FROM t1 WHERE x > 50 AND y like '%64%';
+
+-- aggregate function
+SELECT MIN(x), AVG(x) FROM t1;
+
+-- window function
+SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%';
+
+RESET sepgsql.debug_audit;
+RESET client_min_messages;
+--
+-- Cleanup
+--
+DROP TABLE IF EXISTS t1 CASCADE;
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index da0915b..715365a 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -393,8 +393,11 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
    </para>
 
    <para>
-    For functions, <literal>db_procedure:{execute}</> is defined, but is not
-    checked in this version.
+    For functions, <literal>db_procedure:{execute}</> will be checked when
+    user tries to execute a function as a part of query, or using fast-path
+    invocation. If this function is a trusted procedure, it also checks
+    <literal>db_procedure:{entrypoint}</> permission to check whether it
+    can perform as entrypoint of trusted procedure.
    </para>
 
    <para>
diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c
index bc565eb..3c7d7b0 100644
--- a/src/backend/catalog/objectaccess.c
+++ b/src/backend/catalog/objectaccess.c
@@ -11,6 +11,7 @@
 #include "postgres.h"
 
 #include "catalog/objectaccess.h"
+#include "catalog/pg_proc.h"
 
 /*
  * Hook on object accesses.  This is intended as infrastructure for security
@@ -84,3 +85,19 @@ RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
 						  classId, objectId, subId,
 						  (void *) &pa_arg);
 }
+
+/*
+ * RunFunctionExecHook
+ *
+ * It is entrypoint of OAT_FUNCTION_EXEC event
+ */
+void
+RunFunctionExecHook(Oid objectId)
+{
+	/* XXX - should be checked at caller side */
+	Assert(object_access_hook != NULL);
+
+	(*object_access_hook)(OAT_FUNCTION_EXEC,
+						  ProcedureRelationId, objectId, 0,
+						  NULL);
+}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 62d27a7..5b87f4f 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -39,6 +39,7 @@
 #include "access/htup_details.h"
 #include "access/nbtree.h"
 #include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_type.h"
 #include "commands/typecmds.h"
 #include "executor/execdebug.h"
@@ -1289,6 +1290,7 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 	aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
+	InvokeFunctionExecHook(foid);
 
 	/*
 	 * Safety check on nargs.  Under normal circumstances this should never
@@ -4223,6 +4225,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 		if (aclresult != ACLCHECK_OK)
 			aclcheck_error(aclresult, ACL_KIND_PROC,
 						   get_func_name(acoerce->elemfuncid));
+		InvokeFunctionExecHook(acoerce->elemfuncid);
 
 		/* Set up the primary fmgr lookup information */
 		fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 7efc490..55ab150 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -79,6 +79,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_aggregate.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
@@ -1625,6 +1626,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 		if (aclresult != ACLCHECK_OK)
 			aclcheck_error(aclresult, ACL_KIND_PROC,
 						   get_func_name(aggref->aggfnoid));
+		InvokeFunctionExecHook(aggref->aggfnoid);
 
 		peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
 		peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
@@ -1647,6 +1649,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 			if (aclresult != ACLCHECK_OK)
 				aclcheck_error(aclresult, ACL_KIND_PROC,
 							   get_func_name(transfn_oid));
+			InvokeFunctionExecHook(transfn_oid);
 			if (OidIsValid(finalfn_oid))
 			{
 				aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
@@ -1654,6 +1657,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 				if (aclresult != ACLCHECK_OK)
 					aclcheck_error(aclresult, ACL_KIND_PROC,
 								   get_func_name(finalfn_oid));
+				InvokeFunctionExecHook(finalfn_oid);
 			}
 		}
 
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 3bc42ba..6e76dc6 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -34,6 +34,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_aggregate.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
@@ -1559,6 +1560,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 		if (aclresult != ACLCHECK_OK)
 			aclcheck_error(aclresult, ACL_KIND_PROC,
 						   get_func_name(wfunc->winfnoid));
+		InvokeFunctionExecHook(wfunc->winfnoid);
 
 		/* Fill in the perfuncstate data */
 		perfuncstate->wfuncstate = wfuncstate;
@@ -1767,6 +1769,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
 		if (aclresult != ACLCHECK_OK)
 			aclcheck_error(aclresult, ACL_KIND_PROC,
 						   get_func_name(transfn_oid));
+		InvokeFunctionExecHook(transfn_oid);
 		if (OidIsValid(finalfn_oid))
 		{
 			aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
@@ -1774,6 +1777,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
 			if (aclresult != ACLCHECK_OK)
 				aclcheck_error(aclresult, ACL_KIND_PROC,
 							   get_func_name(finalfn_oid));
+			InvokeFunctionExecHook(finalfn_oid);
 		}
 	}
 
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 21c9797..2e655ed 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -22,6 +22,7 @@
 
 #include "access/htup_details.h"
 #include "access/xact.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_proc.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
@@ -360,6 +361,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error(aclresult, ACL_KIND_PROC,
 					   get_func_name(fid));
+	InvokeFunctionExecHook(fid);
 
 	/*
 	 * Prepare function call info block and insert arguments.
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 25f963b..119d1d1 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -27,6 +27,12 @@
  * hook can use SnapshotNow and SnapshotSelf to get the old and new
  * versions of the tuple.
  *
+ * OAT_FUNCTION_EXEC should be invoked prior to function execution.
+ * This event is almost equivalent to ACL_EXECUTE permission on functions,
+ * but isn't invoked where ACL_EXECUTE is checked during object creation
+ * or altering. (Extensions can check same stuff on OAT_POST_CREATE or
+ * OAT_POST_ALTER events).
+ *
  * Other types may be added in the future.
  */
 typedef enum ObjectAccessType
@@ -34,6 +40,7 @@ typedef enum ObjectAccessType
 	OAT_POST_CREATE,
 	OAT_DROP,
 	OAT_POST_ALTER,
+	OAT_FUNCTION_EXEC,
 } ObjectAccessType;
 
 /*
@@ -101,6 +108,7 @@ extern void RunObjectDropHook(Oid classId, Oid objectId, int subId,
 							  int dropflags);
 extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
 								   Oid auxiliaryId, bool is_internal);
+extern void RunFunctionExecHook(Oid objectId);
 
 /*
  * The following macros are wrappers around the functions above; these should
@@ -137,4 +145,10 @@ extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
 								   (auxiliaryId),(is_internal));	\
 	} while(0)
 
+#define InvokeFunctionExecHook(objectId)		\
+	do {										\
+		if (object_access_hook)					\
+			RunFunctionExecHook(objectId);		\
+	} while(0)
+
 #endif   /* OBJECTACCESS_H */
