unchanged:
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -40,6 +40,7 @@ OBJS = \
 	indexcmds.o \
 	lockcmds.o \
 	matview.o \
+	modulecmds.o \
 	opclasscmds.o \
 	operatorcmds.o \
 	policy.o \
unchanged:
--- /dev/null
+++ b/src/backend/commands/modulecmds.c
@@ -0,0 +1,202 @@
+/*-------------------------------------------------------------------------
+ *
+ * modulecmds.c
+ *	  module creation/manipulation commands
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/commands/modulecmds.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_namespace.h"
+#include "commands/dbcommands.h"
+#include "commands/event_trigger.h"
+#include "commands/modulecmds.h"
+#include "miscadmin.h"
+#include "parser/parse_utilcmd.h"
+#include "tcop/utility.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+/*
+ * CREATE MODULE
+ *
+ * Note: caller should pass in location information for the whole
+ * CREATE MODULE statement, which in turn we pass down as the location
+ * of the component commands.  This comports with our general plan of
+ * reporting location/len for the whole command even when executing
+ * a subquery.
+ */
+ObjectAddress
+CreateModuleCommand(ParseState *pstate, CreateModuleStmt *stmt, const char *queryString,
+					int stmt_location, int stmt_len)
+{
+	char	   *modulename;
+	Oid			namespaceId;
+	Oid			moduleId;
+	OverrideSearchPath *overridePath;
+	List	   *parsetree_list;
+	ListCell   *parsetree_item;
+	Oid			owner_uid;
+	Oid			saved_uid;
+	int			save_sec_context;
+	AclResult	aclresult;
+	ObjectAddress myself,
+				referenced;
+	ObjectAddresses *addrs;
+
+	GetUserIdAndSecContext(&saved_uid, &save_sec_context);
+
+	/*
+	 * Who is supposed to own the new module?
+	 */
+	if (stmt->authrole)
+		owner_uid = get_rolespec_oid(stmt->authrole, false);
+	else
+		owner_uid = saved_uid;
+
+	/* Convert list of names to a name and namespace */
+	namespaceId = QualifiedNameGetCreationNamespace(stmt->modulename,
+													&modulename, false);
+
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, OBJECT_SCHEMA,
+					   get_namespace_name(namespaceId));
+
+	/*
+	 * If if_not_exists was given and the module already exists, bail out.
+	 * (Note: we needn't check this when not if_not_exists, because
+	 * NamespaceCreate will complain anyway.)  We could do this before making
+	 * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
+	 * creation-permission check first, we do likewise.
+	 */
+
+	if (stmt->if_not_exists &&
+		SearchSysCacheExists2(NAMESPACENAME, PointerGetDatum(modulename),
+							  ObjectIdGetDatum(namespaceId)))
+	{
+		ereport(NOTICE,
+				(errcode(ERRCODE_DUPLICATE_SCHEMA),
+				 errmsg("module \"%s\" already exists, skipping",
+						modulename)));
+		return InvalidObjectAddress;
+	}
+
+	/*
+	 * If the requested authorization is different from the current user,
+	 * temporarily set the current user so that the object(s) will be created
+	 * with the correct ownership.
+	 *
+	 * (The setting will be restored at the end of this routine, or in case of
+	 * error, transaction abort will clean things up.)
+	 */
+
+	if (saved_uid != owner_uid)
+		SetUserIdAndSecContext(owner_uid,
+							   save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
+
+	/* Create the module's namespace */
+	moduleId = NamespaceCreate(modulename, namespaceId, NSPKIND_MODULE,
+							   owner_uid, false);
+
+	/* Advance cmd counter to make the namespace visible */
+	CommandCounterIncrement();
+
+	/*
+	 * Temporarily make the new namespace be the front of the search path, as
+	 * well as the default creation target namespace.  This will be undone at
+	 * the end of this routine, or upon error.
+	 */
+	overridePath = GetOverrideSearchPath(CurrentMemoryContext);
+	overridePath->schemas = lcons_oid(moduleId, overridePath->schemas);
+	/* XXX should we clear overridePath->useTemp? */
+	PushOverrideSearchPath(overridePath);
+
+	/*
+	 * Report the new module to possibly interested event triggers.  Note we
+	 * must do this here and not in ProcessUtilitySlow because otherwise the
+	 * objects created below are reported before the module, which would be
+	 * wrong.
+	 */
+	ObjectAddressSet(myself, NamespaceRelationId, moduleId);
+	EventTriggerCollectSimpleCommand(myself, InvalidObjectAddress,
+									 (Node *) stmt);
+
+	/*
+	 * Examine the list of commands embedded in the CREATE MODULE command, and
+	 * reorganize them into a sequentially executable order with no forward
+	 * references.  Note that the result is still a list of raw parsetrees ---
+	 * we cannot, in general, run parse analysis on one statement until we
+	 * have actually executed the prior ones.
+	 */
+	parsetree_list = transformCreateModuleStmt(stmt);
+
+	/*
+	 * Execute each command contained in the CREATE MODULE.  Since the grammar
+	 * allows only utility commands in CREATE MODULE, there is no need to pass
+	 * them through parse_analyze() or the rewriter; we can just hand them
+	 * straight to ProcessUtility.
+	 */
+	foreach(parsetree_item, parsetree_list)
+	{
+		Node	   *stmt = (Node *) lfirst(parsetree_item);
+		PlannedStmt *wrapper;
+
+		wrapper = makeNode(PlannedStmt);
+		wrapper->commandType = CMD_UTILITY;
+		wrapper->canSetTag = false;
+		wrapper->utilityStmt = stmt;
+		wrapper->stmt_location = stmt_location;
+		wrapper->stmt_len = stmt_len;
+
+		ProcessUtility(wrapper,
+					   queryString,
+					   PROCESS_UTILITY_SUBCOMMAND,
+					   NULL,
+					   NULL,
+					   None_Receiver,
+					   NULL);
+
+		CommandCounterIncrement();
+	}
+
+	/* Reset search path to normal state */
+	PopOverrideSearchPath();
+
+	/* Reset current user and security context */
+	SetUserIdAndSecContext(saved_uid, save_sec_context);
+
+	addrs = new_object_addresses();
+
+
+	/* dependency on namespace */
+	ObjectAddressSet(referenced, NamespaceRelationId, namespaceId);
+	add_exact_object_address(&referenced, addrs);
+
+	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	free_object_addresses(addrs);
+
+	recordDependencyOnOwner(NamespaceRelationId, moduleId, owner_uid);
+
+	return myself;
+}
only in patch2:
unchanged:
--- /dev/null
+++ b/src/include/commands/modulecmds.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * modulecmds.h
+ *	  prototypes for modulecmds.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/modulecmds.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef MODULECMDS_H
+#define MODULECMDS_H
+
+#include "catalog/objectaddress.h"
+#include "nodes/parsenodes.h"
+
+extern ObjectAddress	CreateModuleCommand(ParseState *pstate,
+											CreateModuleStmt *parsetree,
+											const char *queryString,
+											int stmt_location, int stmt_len);
+
+#endif							/* MODULECMDS_H */
