From acd602598192b1a0b6c7cd7c77c2a16597f7af99 Mon Sep 17 00:00:00 2001
From: reshke kirill <reshke@double.cloud>
Date: Sat, 11 Jun 2022 11:54:10 +0300
Subject: [PATCH v1] Add create storage manager ddl and routines

add create storage manager ddl and support

pass smgr routines to smgropen

boostrap now work

post boostrap ok

initdb ok

fixes

regression passed with 8 failed tests

add smgr extension and fixes
---
 contrib/rsmgr/Makefile                        |   9 +
 contrib/rsmgr/proxy_smgr--1.0.sql             |  12 ++
 contrib/rsmgr/proxy_smgr.c                    | 123 +++++++++++
 contrib/rsmgr/proxy_smgr.control              |   5 +
 src/backend/access/heap/heapam_handler.c      |   2 +
 src/backend/access/rmgrdesc/heapdesc.c        |   3 +-
 src/backend/access/rmgrdesc/xlogdesc.c        |   4 +-
 src/backend/access/transam/xlogrecovery.c     |   4 +-
 src/backend/bootstrap/bootparse.y             |   3 +
 src/backend/catalog/Makefile                  |   4 +-
 src/backend/catalog/aclchk.c                  |   6 +-
 src/backend/catalog/catalog.c                 |   4 +-
 src/backend/catalog/dependency.c              |   6 +
 src/backend/catalog/heap.c                    |  10 +-
 src/backend/catalog/index.c                   |   7 +-
 src/backend/catalog/objectaddress.c           |  66 ++++++
 src/backend/catalog/toasting.c                |   2 +
 src/backend/commands/Makefile                 |   1 +
 src/backend/commands/alter.c                  |   1 +
 src/backend/commands/cluster.c                |   6 +-
 src/backend/commands/dbcommands.c             |   3 +
 src/backend/commands/event_trigger.c          |   4 +
 src/backend/commands/matview.c                |   2 +-
 src/backend/commands/smgrcmds.c               | 197 ++++++++++++++++++
 src/backend/commands/tablecmds.c              |  29 ++-
 src/backend/nodes/equalfuncs.c                |  13 ++
 src/backend/parser/gram.y                     |  51 ++++-
 src/backend/storage/smgr/Makefile             |   4 +-
 src/backend/storage/smgr/smgr.c               |  84 ++------
 src/backend/storage/smgr/smgr_handler.c       |  75 +++++++
 src/backend/storage/smgr/smgrapi.c            |  96 +++++++++
 src/backend/tcop/utility.c                    |  15 ++
 src/backend/utils/adt/pseudotypes.c           |   1 +
 src/backend/utils/cache/relcache.c            |  59 +++++-
 src/backend/utils/cache/syscache.c            |  23 ++
 src/bin/psql/describe.c                       |   1 +
 src/include/catalog/catalog.h                 |   2 +-
 src/include/catalog/dependency.h              |   1 +
 src/include/catalog/heap.h                    |   2 +
 src/include/catalog/pg_class.h                |   3 +
 src/include/catalog/pg_proc.dat               |  13 ++
 src/include/catalog/pg_smgr.dat               |  17 ++
 src/include/catalog/pg_smgr.h                 |  52 +++++
 src/include/catalog/pg_type.dat               |   5 +
 src/include/commands/cluster.h                |   2 +-
 src/include/commands/defrem.h                 |   5 +
 src/include/nodes/nodes.h                     |   2 +
 src/include/nodes/parsenodes.h                |  14 ++
 src/include/parser/kwlist.h                   |   1 +
 src/include/storage/md.h                      |   3 +
 src/include/storage/relfilenode.h             |   1 +
 src/include/storage/smgr.h                    |  35 ++--
 src/include/tcop/cmdtaglist.h                 |   2 +
 src/include/utils/rel.h                       |   7 +
 src/include/utils/relcache.h                  |   2 +
 src/include/utils/syscache.h                  |   2 +
 .../modules/test_oat_hooks/test_oat_hooks.c   |   6 +
 57 files changed, 998 insertions(+), 114 deletions(-)
 create mode 100644 contrib/rsmgr/Makefile
 create mode 100644 contrib/rsmgr/proxy_smgr--1.0.sql
 create mode 100644 contrib/rsmgr/proxy_smgr.c
 create mode 100644 contrib/rsmgr/proxy_smgr.control
 create mode 100644 src/backend/commands/smgrcmds.c
 create mode 100644 src/backend/storage/smgr/smgr_handler.c
 create mode 100644 src/backend/storage/smgr/smgrapi.c
 create mode 100644 src/include/catalog/pg_smgr.dat
 create mode 100644 src/include/catalog/pg_smgr.h

diff --git a/contrib/rsmgr/Makefile b/contrib/rsmgr/Makefile
new file mode 100644
index 0000000000..d065209bdf
--- /dev/null
+++ b/contrib/rsmgr/Makefile
@@ -0,0 +1,9 @@
+MODULES = proxy_smgr
+
+EXTENSION = proxy_smgr
+DATA = proxy_smgr--1.0.sql
+PGFILEDESC = "proxy_smgr - template table smgra"
+
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
diff --git a/contrib/rsmgr/proxy_smgr--1.0.sql b/contrib/rsmgr/proxy_smgr--1.0.sql
new file mode 100644
index 0000000000..11e743c3dd
--- /dev/null
+++ b/contrib/rsmgr/proxy_smgr--1.0.sql
@@ -0,0 +1,12 @@
+/* proxy_smgr/proxy_smgr--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION proxy_smgr" to load this file. \quit
+
+CREATE FUNCTION proxy_smgr_handler(internal)
+RETURNS table_smgr_handler
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+-- Storage manager
+CREATE STORAGE MANAGER proxy_smgr HANDLER proxy_smgr_handler;
diff --git a/contrib/rsmgr/proxy_smgr.c b/contrib/rsmgr/proxy_smgr.c
new file mode 100644
index 0000000000..955dbd1aea
--- /dev/null
+++ b/contrib/rsmgr/proxy_smgr.c
@@ -0,0 +1,123 @@
+
+#include "postgres.h"
+
+#include <math.h>
+
+#include "miscadmin.h"
+
+#include "access/tableam.h"
+#include "access/heapam.h"
+#include "access/amapi.h"
+#include "catalog/index.h"
+#include "commands/vacuum.h"
+#include "storage/smgr.h"
+#include "storage/md.h"
+#include "utils/elog.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(proxy_smgr_handler);
+
+
+/* ------------------------------------------------------------------------
+ * Definition of the blackhole table access method.
+ * ------------------------------------------------------------------------
+ */
+
+void proxyinit(void) {
+	elog(INFO, "proxy init");
+	mdinit();
+}
+
+void proxyopen(SMgrRelation reln) {
+	elog(INFO, "proxy open %d %d %d", reln->smgr_rnode.node.spcNode, reln->smgr_rnode.node.dbNode, reln->smgr_rnode.node.relNode);
+	mdopen(reln);
+}
+
+void proxyclose(SMgrRelation reln, ForkNumber forknum) {
+	elog(INFO, "proxy close, %d", reln->smgr_rnode.node.relNode);
+	mdclose(reln, forknum);
+}
+
+void proxycreate(SMgrRelation reln, ForkNumber forknum, bool isRedo) {
+	elog(INFO, "proxy create %d", reln->smgr_rnode.node.relNode);
+
+	mdcreate(reln, forknum, isRedo);
+}
+
+bool proxyexists(SMgrRelation reln, ForkNumber forknum) {
+	elog(INFO, "proxy exists %d", reln->smgr_rnode.node.relNode);
+	return mdexists(reln, forknum);
+}
+
+void proxyunlink(RelFileNodeBackend rnode, ForkNumber forknum, bool isRedo) {
+	elog(INFO, "proxy unlink %d", rnode.node.relNode);
+	mdunlink(rnode, forknum, isRedo);
+}
+
+void proxyextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync) {
+	elog(INFO, "proxcy extend %d", reln->smgr_rnode.node.relNode);
+	mdextend(reln, forknum, blocknum, buffer, skipFsync);
+}
+
+bool proxyprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum) {
+	elog(INFO, "proxy prefecth %d", reln->smgr_rnode.node.relNode);
+	mdprefetch(reln, forknum, blocknum);
+}
+
+void proxyread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer) {
+	elog(INFO, "proxy read %d", reln->smgr_rnode.node.relSmgr);
+	mdread(reln, forknum, blocknum, buffer);
+}
+
+void proxywrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync) {
+	elog(INFO, "proxy write %d", reln->smgr_rnode.node.relNode);
+	mdwrite(reln, forknum, blocknum, buffer, skipFsync);
+}
+
+void proxywriteback(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, BlockNumber nblocks) {
+	elog(INFO, "proxy writeback %d", reln->smgr_rnode.node.relNode);	
+	mdwriteback(reln, forknum, blocknum, nblocks);
+}
+
+BlockNumber proxynblocks(SMgrRelation reln, ForkNumber forknum) {
+	elog(INFO, "proxy nblocks %d", reln->smgr_rnode.node.relNode);	
+	return mdnblocks(reln, forknum);
+}
+
+void proxytruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks) {
+	elog(INFO, "proxy truncate %d", reln->smgr_rnode.node.relNode);	
+	return mdtruncate(reln, forknum, nblocks);
+}
+
+void proxyimmedsync(SMgrRelation reln, ForkNumber forknum) {
+	elog(INFO, "proxy immedsync %d", reln->smgr_rnode.node.relNode);	
+	return mdnblocks(reln, forknum);
+}
+
+static const TableSmgr proxy_smgr_methods = {
+	.type = T_TableSmgr,
+
+	.smgr_init = proxyinit,
+	.smgr_shutdown = NULL,
+	.smgr_open = proxyopen,
+	.smgr_close = proxyclose,
+	.smgr_create = proxycreate,
+	.smgr_exists = proxyexists,
+	.smgr_unlink = proxyunlink,
+	.smgr_extend = proxyextend,
+	.smgr_prefetch = proxyprefetch,
+	.smgr_read = proxyread,
+	.smgr_write = proxywrite,
+	.smgr_writeback = proxywriteback,
+	.smgr_nblocks = proxynblocks,
+	.smgr_truncate = proxytruncate,
+	.smgr_immedsync = proxyimmedsync,
+};
+
+
+Datum
+proxy_smgr_handler(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_POINTER(&proxy_smgr_methods);
+}
diff --git a/contrib/rsmgr/proxy_smgr.control b/contrib/rsmgr/proxy_smgr.control
new file mode 100644
index 0000000000..053d67d74a
--- /dev/null
+++ b/contrib/rsmgr/proxy_smgr.control
@@ -0,0 +1,5 @@
+# proxy_smgr extension
+comment = 'template table SMGR'
+default_version = '1.0'
+module_pathname = '$libdir/proxy_smgr'
+relocatable = true
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 444f027149..bad035f39e 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1190,6 +1190,7 @@ heapam_index_build_range_scan(Relation heapRelation,
 	 * sanity checks
 	 */
 	Assert(OidIsValid(indexRelation->rd_rel->relam));
+	Assert(OidIsValid(indexRelation->rd_rel->relsmgr));
 
 	/* Remember if it's a system catalog */
 	is_system_catalog = IsSystemRelation(heapRelation);
@@ -1760,6 +1761,7 @@ heapam_index_validate_scan(Relation heapRelation,
 	 * sanity checks
 	 */
 	Assert(OidIsValid(indexRelation->rd_rel->relam));
+	Assert(OidIsValid(indexRelation->rd_rel->relsmgr));
 
 	/*
 	 * Need an EState for evaluation of index expressions and partial-index
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
index 6238085d65..2af6d3e544 100644
--- a/src/backend/access/rmgrdesc/heapdesc.c
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -169,10 +169,11 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
 	{
 		xl_heap_new_cid *xlrec = (xl_heap_new_cid *) rec;
 
-		appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",
+		appendStringInfo(buf, "rel %u/%u/%u/%u; tid %u/%u",
 						 xlrec->target_node.spcNode,
 						 xlrec->target_node.dbNode,
 						 xlrec->target_node.relNode,
+						 xlrec->target_node.relSmgr,
 						 ItemPointerGetBlockNumber(&(xlrec->target_tid)),
 						 ItemPointerGetOffsetNumber(&(xlrec->target_tid)));
 		appendStringInfo(buf, "; cmin: %u, cmax: %u, combo: %u",
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index fefc563323..18f0da9aa1 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -306,9 +306,9 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 			else
 			{
 				appendStringInfo(buf,
-								 ", blkref #%d: rel %u/%u/%u blk %u",
+								 ", blkref #%d: rel %u/%u/%u/%u blk %u",
 								 block_id,
-								 rnode.spcNode, rnode.dbNode, rnode.relNode,
+								 rnode.spcNode, rnode.dbNode, rnode.relNode, rnode.relSmgr,
 								 blk);
 			}
 
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 6eba626420..11a6adda17 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -2181,9 +2181,9 @@ xlog_block_info(StringInfo buf, XLogReaderState *record)
 							 forknum,
 							 blk);
 		else
-			appendStringInfo(buf, "; blkref #%d: rel %u/%u/%u, blk %u",
+			appendStringInfo(buf, "; blkref #%d: rel %u/%u/%u/%u, blk %u",
 							 block_id,
-							 rnode.spcNode, rnode.dbNode, rnode.relNode,
+							 rnode.spcNode, rnode.dbNode, rnode.relNode, rnode.relSmgr,
 							 blk);
 		if (XLogRecHasBlockImage(record, block_id))
 			appendStringInfoString(buf, " FPW");
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index e5cf1b3d43..3a49595fdd 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -25,6 +25,7 @@
 #include "catalog/pg_authid.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/toasting.h"
 #include "commands/defrem.h"
@@ -205,6 +206,7 @@ Boot_CreateStmt:
 												   $3,
 												   InvalidOid,
 												   HEAP_TABLE_AM_OID,
+												   SMGR_MD_OID,
 												   tupdesc,
 												   RELKIND_RELATION,
 												   RELPERSISTENCE_PERMANENT,
@@ -228,6 +230,7 @@ Boot_CreateStmt:
 													  InvalidOid,
 													  BOOTSTRAP_SUPERUSERID,
 													  HEAP_TABLE_AM_OID,
+													  SMGR_MD_OID,
 													  tupdesc,
 													  NIL,
 													  RELKIND_RELATION,
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 89a0221ec9..8869bc9961 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -72,7 +72,7 @@ CATALOG_HEADERS := \
 	pg_collation.h pg_parameter_acl.h pg_partitioned_table.h \
 	pg_range.h pg_transform.h \
 	pg_sequence.h pg_publication.h pg_publication_namespace.h \
-	pg_publication_rel.h pg_subscription.h pg_subscription_rel.h
+	pg_publication_rel.h pg_subscription.h pg_subscription_rel.h pg_smgr.h
 
 GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h system_fk_info.h
 
@@ -86,7 +86,7 @@ POSTGRES_BKI_DATA = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_namespace.dat pg_opclass.dat pg_operator.dat pg_opfamily.dat \
 	pg_proc.dat pg_range.dat pg_tablespace.dat \
 	pg_ts_config.dat pg_ts_config_map.dat pg_ts_dict.dat pg_ts_parser.dat \
-	pg_ts_template.dat pg_type.dat \
+	pg_ts_template.dat pg_type.dat pg_smgr.dat \
 	)
 
 all: distprep generated-header-symlinks
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 5f1726c095..ba727d0b2d 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_parameter_acl.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_transform.h"
@@ -3638,6 +3639,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
 					case OBJECT_PUBLICATION_REL:
 					case OBJECT_ROLE:
 					case OBJECT_RULE:
+					case OBJECT_STORAGE_MANAGER:
 					case OBJECT_TABCONSTRAINT:
 					case OBJECT_TRANSFORM:
 					case OBJECT_TRIGGER:
@@ -3778,6 +3780,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
 					case OBJECT_PUBLICATION_NAMESPACE:
 					case OBJECT_PUBLICATION_REL:
 					case OBJECT_ROLE:
+					case OBJECT_STORAGE_MANAGER:
 					case OBJECT_TRANSFORM:
 					case OBJECT_TSPARSER:
 					case OBJECT_TSTEMPLATE:
@@ -6254,7 +6257,8 @@ recordExtObjInitPriv(Oid objoid, Oid classoid)
 			 classoid == TSDictionaryRelationId ||
 			 classoid == TSParserRelationId ||
 			 classoid == TSTemplateRelationId ||
-			 classoid == TransformRelationId
+			 classoid == TransformRelationId || 
+			 classoid == StorageManagerRelationId
 		)
 	{
 		/* no ACL for these object types, so do nothing. */
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index e784538aae..9029ecf756 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -497,7 +497,7 @@ GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
  * created by bootstrap have preassigned OIDs, so there's no need.
  */
 Oid
-GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
+GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence, Oid relsmgr)
 {
 	RelFileNodeBackend rnode;
 	char	   *rpath;
@@ -536,6 +536,8 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 	 */
 	rnode.backend = backend;
 
+	rnode.node.relSmgr = relsmgr;
+
 	do
 	{
 		CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index de10923391..285a728ad0 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_publication_rel.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_transform.h"
@@ -185,6 +186,7 @@ static const Oid object_classes[] = {
 	PublicationRelationId,		/* OCLASS_PUBLICATION */
 	PublicationRelRelationId,	/* OCLASS_PUBLICATION_REL */
 	SubscriptionRelationId,		/* OCLASS_SUBSCRIPTION */
+	StorageManagerRelationId,   /* OCLASS_SMGR */
 	TransformRelationId			/* OCLASS_TRANSFORM */
 };
 
@@ -1490,6 +1492,7 @@ doDeletion(const ObjectAddress *object, int flags)
 		case OCLASS_AMOP:
 		case OCLASS_AMPROC:
 		case OCLASS_SCHEMA:
+		case OCLASS_SMGR:
 		case OCLASS_TSPARSER:
 		case OCLASS_TSDICT:
 		case OCLASS_TSTEMPLATE:
@@ -2882,6 +2885,9 @@ getObjectClass(const ObjectAddress *object)
 		case SubscriptionRelationId:
 			return OCLASS_SUBSCRIPTION;
 
+		case StorageManagerRelationId:
+			return OCLASS_SMGR;
+
 		case TransformRelationId:
 			return OCLASS_TRANSFORM;
 	}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 1803194db9..0ad8f31f4f 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -50,6 +50,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_partitioned_table.h"
 #include "catalog/pg_statistic.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_subscription_rel.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -291,6 +292,7 @@ heap_create(const char *relname,
 			Oid relid,
 			Oid relfilenode,
 			Oid accessmtd,
+			Oid smgrid,
 			TupleDesc tupDesc,
 			char relkind,
 			char relpersistence,
@@ -368,6 +370,7 @@ heap_create(const char *relname,
 									 tupDesc,
 									 relid,
 									 accessmtd,
+									 smgrid,
 									 relfilenode,
 									 reltablespace,
 									 shared_relation,
@@ -898,6 +901,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 	values[Anum_pg_class_reloftype - 1] = ObjectIdGetDatum(rd_rel->reloftype);
 	values[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(rd_rel->relowner);
 	values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(rd_rel->relam);
+	values[Anum_pg_class_relsmgr - 1] = ObjectIdGetDatum(rd_rel->relsmgr);
 	values[Anum_pg_class_relfilenode - 1] = ObjectIdGetDatum(rd_rel->relfilenode);
 	values[Anum_pg_class_reltablespace - 1] = ObjectIdGetDatum(rd_rel->reltablespace);
 	values[Anum_pg_class_relpages - 1] = Int32GetDatum(rd_rel->relpages);
@@ -1093,6 +1097,7 @@ heap_create_with_catalog(const char *relname,
 						 Oid reloftypeid,
 						 Oid ownerid,
 						 Oid accessmtd,
+						 Oid smgrid,
 						 TupleDesc tupdesc,
 						 List *cooked_constraints,
 						 char relkind,
@@ -1230,7 +1235,7 @@ heap_create_with_catalog(const char *relname,
 
 		if (!OidIsValid(relid))
 			relid = GetNewRelFileNode(reltablespace, pg_class_desc,
-									  relpersistence);
+									  relpersistence, smgrid);
 	}
 
 	/*
@@ -1275,6 +1280,7 @@ heap_create_with_catalog(const char *relname,
 							   relid,
 							   relfilenode,
 							   accessmtd,
+							   smgrid,
 							   tupdesc,
 							   relkind,
 							   relpersistence,
@@ -1455,6 +1461,8 @@ heap_create_with_catalog(const char *relname,
 			add_exact_object_address(&referenced, addrs);
 		}
 
+		ObjectAddressSet(referenced, StorageManagerRelationId, smgrid);
+
 		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 		free_object_addresses(addrs);
 	}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index bdd3c34841..45459d8fe7 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -50,6 +50,7 @@
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -728,6 +729,7 @@ index_create(Relation heapRelation,
 	Oid			namespaceId;
 	int			i;
 	char		relpersistence;
+	Oid			relsmgr;
 	bool		isprimary = (flags & INDEX_CREATE_IS_PRIMARY) != 0;
 	bool		invalid = (flags & INDEX_CREATE_INVALID) != 0;
 	bool		concurrent = (flags & INDEX_CREATE_CONCURRENT) != 0;
@@ -758,6 +760,7 @@ index_create(Relation heapRelation,
 	shared_relation = heapRelation->rd_rel->relisshared;
 	mapped_relation = RelationIsMapped(heapRelation);
 	relpersistence = heapRelation->rd_rel->relpersistence;
+	relsmgr = heapRelation->rd_rel->relsmgr;
 
 	/*
 	 * check parameters
@@ -937,7 +940,7 @@ index_create(Relation heapRelation,
 		else
 		{
 			indexRelationId =
-				GetNewRelFileNode(tableSpaceId, pg_class, relpersistence);
+				GetNewRelFileNode(tableSpaceId, pg_class, relpersistence, relsmgr);
 		}
 	}
 
@@ -952,6 +955,7 @@ index_create(Relation heapRelation,
 								indexRelationId,
 								relFileNode,
 								accessMethodObjectId,
+								SMGR_MD_OID,
 								indexTupDesc,
 								relkind,
 								relpersistence,
@@ -981,6 +985,7 @@ index_create(Relation heapRelation,
 	 */
 	indexRelation->rd_rel->relowner = heapRelation->rd_rel->relowner;
 	indexRelation->rd_rel->relam = accessMethodObjectId;
+	indexRelation->rd_rel->relsmgr = relsmgr;
 	indexRelation->rd_rel->relispartition = OidIsValid(parentIndexRelid);
 
 	/*
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8377b4f7d4..5c39309aa2 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -53,6 +53,7 @@
 #include "catalog/pg_publication_rel.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_transform.h"
@@ -414,6 +415,20 @@ static const ObjectPropertyType ObjectProperty[] =
 		OBJECT_SCHEMA,
 		true
 	},
+	{
+		"storage manager",
+		StorageManagerRelationId,
+		SmgrOidIndexId,
+		SMGROID,
+		SMGRNAME,
+		Anum_pg_smgr_oid,
+		Anum_pg_smgr_smgrname,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		-1,
+		true
+	},
 	{
 		"relation",
 		RelationRelationId,
@@ -843,6 +858,10 @@ static const struct object_type_map
 	{
 		"subscription", OBJECT_SUBSCRIPTION
 	},
+	/* OCLASS_SMGR */
+	{
+		"storage manager", OBJECT_STORAGE_MANAGER
+	},
 	/* OCLASS_TRANSFORM */
 	{
 		"transform", OBJECT_TRANSFORM
@@ -1335,6 +1354,11 @@ get_object_address_unqualified(ObjectType objtype,
 			address.objectId = get_subscription_oid(name, missing_ok);
 			address.objectSubId = 0;
 			break;
+		case OBJECT_STORAGE_MANAGER:
+			address.classId = StorageManagerRelationId;
+			address.objectId = get_smgr_oid(name, missing_ok);
+			address.objectSubId = 0;
+			break;
 		default:
 			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 			/* placate compiler, which doesn't know elog won't return */
@@ -2318,6 +2342,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 		case OBJECT_ROLE:
 		case OBJECT_SCHEMA:
 		case OBJECT_SUBSCRIPTION:
+		case OBJECT_STORAGE_MANAGER:
 		case OBJECT_TABLESPACE:
 			if (list_length(name) != 1)
 				ereport(ERROR,
@@ -3261,6 +3286,26 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
 				break;
 			}
 
+		case OCLASS_SMGR:
+			{
+				HeapTuple	tup;
+
+				tup = SearchSysCache1(SMGROID,
+									  ObjectIdGetDatum(object->objectId));
+				if (!HeapTupleIsValid(tup))
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for storage manager %u",
+							 object->objectId);
+					break;
+				}
+
+				appendStringInfo(&buffer, _("storage manager %s"),
+								 NameStr(((Form_pg_smgr) GETSTRUCT(tup))->smgrname));
+				ReleaseSysCache(tup);
+				break;
+			}
+
 		case OCLASS_AMOP:
 			{
 				Relation	amopDesc;
@@ -4493,6 +4538,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
 			appendStringInfoString(&buffer, "access method");
 			break;
 
+		case OCLASS_SMGR:
+			appendStringInfoString(&buffer, "storage manager");
+			break;
+
 		case OCLASS_AMOP:
 			appendStringInfoString(&buffer, "operator of access method");
 			break;
@@ -5121,6 +5170,23 @@ getObjectIdentityParts(const ObjectAddress *object,
 					*objname = list_make1(amname);
 			}
 			break;
+		case OCLASS_SMGR:
+			{
+				char	   *smgrname;
+
+				smgrname = get_smgr_name(object->objectId);
+				if (!smgrname)
+				{
+					if (!missing_ok)
+						elog(ERROR, "cache lookup failed for storage mamnager %u",
+							 object->objectId);
+					break;
+				}
+				appendStringInfoString(&buffer, quote_identifier(smgrname));
+				if (objname)
+					*objname = list_make1(smgrname);
+			}
+			break;
 
 		case OCLASS_AMOP:
 			{
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 9bc10729b0..99763185ac 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -27,6 +27,7 @@
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/toasting.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -254,6 +255,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   InvalidOid,
 										   rel->rd_rel->relowner,
 										   table_relation_toast_am(rel),
+										   SMGR_MD_OID,
 										   tupdesc,
 										   NIL,
 										   RELKIND_TOASTVALUE,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 48f7348f91..8f0c71f8d7 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -51,6 +51,7 @@ OBJS = \
 	seclabel.o \
 	sequence.o \
 	statscmds.o \
+	smgrcmds.o \
 	subscriptioncmds.o \
 	tablecmds.o \
 	tablespace.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 5456b8222b..e29a3bd20e 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -663,6 +663,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
 		case OCLASS_PUBLICATION:
 		case OCLASS_PUBLICATION_NAMESPACE:
 		case OCLASS_PUBLICATION_REL:
+		case OCLASS_SMGR:
 		case OCLASS_SUBSCRIPTION:
 		case OCLASS_TRANSFORM:
 			/* ignore object types that don't have schema-qualified names */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index cea2c8be80..7ab7160e06 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -632,6 +632,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
 {
 	Oid			tableOid = RelationGetRelid(OldHeap);
 	Oid			accessMethod = OldHeap->rd_rel->relam;
+	Oid			storageMethod = OldHeap->rd_rel->relsmgr;
 	Oid			tableSpace = OldHeap->rd_rel->reltablespace;
 	Oid			OIDNewHeap;
 	char		relpersistence;
@@ -654,7 +655,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
 	/* Create the transient table that will receive the re-ordered data */
 	OIDNewHeap = make_new_heap(tableOid, tableSpace,
 							   accessMethod,
-							   relpersistence,
+							   relpersistence, storageMethod,
 							   AccessExclusiveLock);
 
 	/* Copy the heap data into the new table in the desired order */
@@ -683,7 +684,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
  * data, then call finish_heap_swap to complete the operation.
  */
 Oid
-make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
+make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, Oid NewStorageMethod,
 			  char relpersistence, LOCKMODE lockmode)
 {
 	TupleDesc	OldHeapDesc;
@@ -744,6 +745,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
 										  InvalidOid,
 										  OldHeap->rd_rel->relowner,
 										  NewAccessMethod,
+										  NewStorageMethod,
 										  OldHeapDesc,
 										  NIL,
 										  RELKIND_RELATION,
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index f269168401..81e5bbe5b9 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -195,6 +195,7 @@ CreateDatabaseUsingWalLog(Oid src_dboid, Oid dst_dboid,
 
 		dstrnode.dbNode = dst_dboid;
 		dstrnode.relNode = srcrnode.relNode;
+		dstrnode.relSmgr = srcrnode.relSmgr;
 
 		/*
 		 * Acquire locks on source and target relations before copying.
@@ -271,6 +272,7 @@ ScanSourceDatabasePgClass(Oid tbid, Oid dbid, char *srcpath)
 	rnode.spcNode = tbid;
 	rnode.dbNode = dbid;
 	rnode.relNode = relfilenode;
+	rnode.relSmgr = F_SMGR_MD_HANDLER;
 
 	/*
 	 * We can't use a real relcache entry for a relation in some other
@@ -441,6 +443,7 @@ ScanSourceDatabasePgClassTuple(HeapTupleData *tuple, Oid tbid, Oid dbid,
 
 	relinfo->rnode.dbNode = dbid;
 	relinfo->rnode.relNode = relfilenode;
+	relinfo->rnode.relSmgr = F_SMGR_MD_HANDLER;
 	relinfo->reloid = classForm->oid;
 
 	/* Temporary relations were rejected above. */
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4642527881..6bfed2d6dd 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -982,6 +982,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_SEQUENCE:
 		case OBJECT_SUBSCRIPTION:
 		case OBJECT_STATISTIC_EXT:
+		case OBJECT_STORAGE_MANAGER:
 		case OBJECT_TABCONSTRAINT:
 		case OBJECT_TABLE:
 		case OBJECT_TRANSFORM:
@@ -1042,6 +1043,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
 		case OCLASS_TRIGGER:
 		case OCLASS_SCHEMA:
 		case OCLASS_STATISTIC_EXT:
+		case OCLASS_SMGR:
 		case OCLASS_TSPARSER:
 		case OCLASS_TSDICT:
 		case OCLASS_TSTEMPLATE:
@@ -2080,6 +2082,7 @@ stringify_grant_objtype(ObjectType objtype)
 		case OBJECT_PUBLICATION_REL:
 		case OBJECT_ROLE:
 		case OBJECT_RULE:
+		case OBJECT_STORAGE_MANAGER:
 		case OBJECT_STATISTIC_EXT:
 		case OBJECT_SUBSCRIPTION:
 		case OBJECT_TABCONSTRAINT:
@@ -2164,6 +2167,7 @@ stringify_adefprivs_objtype(ObjectType objtype)
 		case OBJECT_PUBLICATION_REL:
 		case OBJECT_ROLE:
 		case OBJECT_RULE:
+		case OBJECT_STORAGE_MANAGER:
 		case OBJECT_STATISTIC_EXT:
 		case OBJECT_SUBSCRIPTION:
 		case OBJECT_TABCONSTRAINT:
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d1ee106465..d3ae12c835 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -298,7 +298,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
 	 * will be gone).
 	 */
 	OIDNewHeap = make_new_heap(matviewOid, tableSpace,
-							   matviewRel->rd_rel->relam,
+							   matviewRel->rd_rel->relam, matviewRel->rd_rel->relsmgr,
 							   relpersistence, ExclusiveLock);
 	LockRelationOid(OIDNewHeap, AccessExclusiveLock);
 	dest = CreateTransientRelDestReceiver(OIDNewHeap);
diff --git a/src/backend/commands/smgrcmds.c b/src/backend/commands/smgrcmds.c
new file mode 100644
index 0000000000..becfab4cfa
--- /dev/null
+++ b/src/backend/commands/smgrcmds.c
@@ -0,0 +1,197 @@
+
+/*-------------------------------------------------------------------------
+ *
+ * smgrcmds.c
+ *	  Routines for SQL commands that manipulate storage managers.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/commands/smgrcmds.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_smgr.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "catalog/objectaccess.h"
+
+
+
+/*
+ * Convert a handler function name to an Oid.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+lookup_smgr_handler_func(List *handler_name)
+{
+	Oid			handlerOid;
+	Oid			funcargtypes[1] = {INTERNALOID};
+	Oid			expectedType = TABLE_SMGR_HANDLEROID;
+
+	if (handler_name == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("handler function is not specified")));
+
+	/* handlers have one argument of type internal */
+	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+
+	if (get_func_rettype(handlerOid) != expectedType)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("function %s must return type %s",
+						get_func_name(handlerOid),
+						format_type_extended(expectedType, -1, 0))));
+
+	return handlerOid;
+}
+
+/*
+ * CreateStorageManager
+ *		Registers a new storage manager.
+ */
+ObjectAddress
+CreateStorageManager(CreateSmgrStmt *stmt) {
+
+    Relation	rel;
+	ObjectAddress retval;
+	ObjectAddress referenced;
+	Oid			smgroid;
+	Oid			smgrhandler;
+	bool		nulls[Natts_pg_smgr];
+	Datum		values[Natts_pg_smgr];
+	HeapTuple	tup;
+
+	rel = table_open(StorageManagerRelationId, RowExclusiveLock);
+
+	/* Must be superuser */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied to create storage manager \"%s\"",
+						stmt->smgrname),
+				 errhint("Must be superuser to create an storage manager.")));
+
+	/* Check if name is used */
+	smgroid = GetSysCacheOid1(SMGRNAME, Anum_pg_smgr_oid,
+							CStringGetDatum(stmt->smgrname));
+	if (OidIsValid(smgroid))
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("storage manager \"%s\" already exists",
+						stmt->smgrname)));
+	}
+
+	/*
+	 * Get the handler function oid.
+	 */
+	smgrhandler = lookup_smgr_handler_func(stmt->handler_name);
+
+	/*
+	 * Insert tuple into pg_am.
+	 */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+
+	smgroid = GetNewOidWithIndex(rel, SmgrOidIndexId, Anum_pg_smgr_oid);
+	values[Anum_pg_smgr_oid - 1] = ObjectIdGetDatum(smgroid);
+	values[Anum_pg_smgr_smgrname - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(stmt->smgrname));
+	values[Anum_pg_smgr_smgrhandler - 1] = ObjectIdGetDatum(smgrhandler);
+	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+	CatalogTupleInsert(rel, tup);
+	heap_freetuple(tup);
+
+	retval.classId = StorageManagerRelationId;
+	retval.objectId = smgroid;
+	retval.objectSubId = 0;
+
+	/* Record dependency on handler function */
+	referenced.classId = ProcedureRelationId;
+	referenced.objectId = smgrhandler;
+	referenced.objectSubId = 0;
+
+	recordDependencyOn(&retval, &referenced, DEPENDENCY_NORMAL);
+
+	recordDependencyOnCurrentExtension(&retval, false);
+
+	InvokeObjectPostCreateHook(StorageManagerRelationId, smgroid, 0);
+
+	table_close(rel, RowExclusiveLock);
+
+	return retval;
+}
+
+
+
+/*
+ * get_am_type_oid
+ *		Worker for various get_am_*_oid variants
+ *
+ * If missing_ok is false, throw an error if access method not found.  If
+ * true, just return InvalidOid.
+ *
+ * If amtype is not '\0', an error is raised if the AM found is not of the
+ * given type.
+ */
+Oid
+get_smgr_oid(const char *smgrname, bool missing_ok)
+{
+	HeapTuple	tup;
+	Oid			oid = InvalidOid;
+
+	tup = SearchSysCache1(SMGRNAME, CStringGetDatum(smgrname));
+	if (HeapTupleIsValid(tup))
+	{
+		Form_pg_smgr smgrform = (Form_pg_smgr) GETSTRUCT(tup);
+		oid = smgrform->oid;
+		ReleaseSysCache(tup);
+	}
+
+	if (!OidIsValid(oid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("storage manager \"%s\" does not exist", smgrname)));
+	return oid;
+}
+
+
+
+/*
+ * get_smgr_name - given an storage manager OID, look up its name.
+ */
+char *
+get_smgr_name(Oid amOid)
+{
+	HeapTuple	tup;
+	char	   *result = NULL;
+
+	tup = SearchSysCache1(SMGROID, ObjectIdGetDatum(amOid));
+	if (HeapTupleIsValid(tup))
+	{
+		Form_pg_smgr	smgrform = (Form_pg_smgr) GETSTRUCT(tup);
+
+		result = pstrdup(NameStr(smgrform->smgrname));
+		ReleaseSysCache(tup);
+	}
+	return result;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2de0ebacec..70712e5308 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -43,6 +43,7 @@
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -179,6 +180,7 @@ typedef struct AlteredTableInfo
 	bool		verify_new_notnull; /* T if we should recheck NOT NULL */
 	int			rewrite;		/* Reason for forced rewrite, if any */
 	Oid			newAccessMethod;	/* new access method; 0 means no change */
+	Oid			newStorageManager;	/* new storage manager; 0 means no change */
 	Oid			newTableSpace;	/* new tablespace; 0 means no change */
 	bool		chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
 	char		newrelpersistence;	/* if above is true */
@@ -678,6 +680,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	LOCKMODE	parentLockmode;
 	const char *accessMethod = NULL;
 	Oid			accessMethodId = InvalidOid;
+	const char *storageManager = NULL;
+	Oid         smgrOid        = SMGR_MD_OID;
 
 	/*
 	 * Truncate relname to appropriate length (probably a waste of time, as
@@ -953,6 +957,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	if (accessMethod != NULL)
 		accessMethodId = get_table_am_oid(accessMethod, false);
 
+	if (stmt->storageManager != NULL) {
+		storageManager = stmt->storageManager;
+	}
+
+	if (storageManager) {
+		smgrOid = get_smgr_oid(storageManager, false);
+	}
+
 	/*
 	 * Create the relation.  Inherited defaults and constraints are passed in
 	 * for immediate handling --- since they don't need parsing, they can be
@@ -966,6 +978,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 										  ofTypeId,
 										  ownerId,
 										  accessMethodId,
+										  smgrOid,
 										  descriptor,
 										  list_concat(cookedDefaults,
 													  old_constraints),
@@ -5429,6 +5442,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 			Relation	OldHeap;
 			Oid			OIDNewHeap;
 			Oid			NewAccessMethod;
+			Oid			NewStorageManager;
 			Oid			NewTableSpace;
 			char		persistence;
 
@@ -5478,6 +5492,16 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 			else
 				NewAccessMethod = OldHeap->rd_rel->relam;
 
+			/*
+			 * Select destination storage manager (same as original unless user
+			 * requested a change)
+			 */
+			if (OidIsValid(tab->newStorageManager))
+				NewStorageManager = tab->newStorageManager;
+			else
+				NewStorageManager = OldHeap->rd_rel->relsmgr;
+
+
 			/*
 			 * Select persistence of transient table (same as original unless
 			 * user requested a change)
@@ -5517,7 +5541,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 			 * persistence. That wouldn't work for pg_class, but that can't be
 			 * unlogged anyway.
 			 */
-			OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
+			OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod, NewStorageManager,
 									   persistence, lockmode);
 
 			/*
@@ -12683,6 +12707,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 			case OCLASS_PUBLICATION:
 			case OCLASS_PUBLICATION_NAMESPACE:
 			case OCLASS_PUBLICATION_REL:
+			case OCLASS_SMGR:
 			case OCLASS_SUBSCRIPTION:
 			case OCLASS_TRANSFORM:
 
@@ -14374,7 +14399,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	 * to allocate a new one in the new tablespace.
 	 */
 	newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
-									   rel->rd_rel->relpersistence);
+									   rel->rd_rel->relpersistence, rel->rd_rel->relsmgr);
 
 	/* Open old and new relation */
 	newrnode = rel->rd_node;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e747e1667d..88ff3c8fc5 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2433,6 +2433,16 @@ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
 	return true;
 }
 
+
+static bool
+_equalCreateSmgrStmt(const CreateSmgrStmt *a, const CreateSmgrStmt *b)
+{
+	COMPARE_STRING_FIELD(smgrname);
+	COMPARE_NODE_FIELD(handler_name);
+
+	return true;
+}
+
 static bool
 _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 {
@@ -4120,6 +4130,9 @@ equal(const void *a, const void *b)
 		case T_CreateAmStmt:
 			retval = _equalCreateAmStmt(a, b);
 			break;
+		case T_CreateSmgrStmt:
+			retval = _equalCreateSmgrStmt(a, b);
+			break;
 		case T_CreateTrigStmt:
 			retval = _equalCreateTrigStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 969c9c158f..77038e33d4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -328,7 +328,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		AlterTSConfigurationStmt AlterTSDictionaryStmt
 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
 		CreatePublicationStmt AlterPublicationStmt
-		CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt
+		CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt CreateSmgrStmt
 
 %type <node>	select_no_parens select_with_parens select_clause
 				simple_select values_clause
@@ -606,7 +606,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	constraints_set_list
 %type <boolean> constraints_set_mode
-%type <str>		OptTableSpace OptConsTableSpace
+%type <str>		OptTableSpace OptConsTableSpace OptStorageManager
 %type <rolespec> OptTableSpaceOwner
 %type <ival>	opt_check_option
 
@@ -814,7 +814,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
 	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
 
-	MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD
+	MANAGER MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD
 	MINUTE_P MINVALUE MODE MONTH_P MOVE
 
 	NAME_P NAMES NATIONAL NATURAL NCHAR NESTED NEW NEXT NFC NFD NFKC NFKD NO
@@ -1112,6 +1112,7 @@ stmt:
 			| CreatePLangStmt
 			| CreateSchemaStmt
 			| CreateSeqStmt
+			| CreateSmgrStmt
 			| CreateStmt
 			| CreateSubscriptionStmt
 			| CreateStatsStmt
@@ -3578,7 +3579,7 @@ copy_generic_opt_arg_list_item:
 
 CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 			OptInherit OptPartitionSpec table_access_method_clause OptWith
-			OnCommitOption OptTableSpace
+			OnCommitOption OptTableSpace OptStorageManager
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 
@@ -3593,12 +3594,13 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->options = $11;
 					n->oncommit = $12;
 					n->tablespacename = $13;
+					n->storageManager = $14;
 					n->if_not_exists = false;
 					$$ = (Node *) n;
 				}
 		| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '('
 			OptTableElementList ')' OptInherit OptPartitionSpec table_access_method_clause
-			OptWith OnCommitOption OptTableSpace
+			OptWith OnCommitOption OptTableSpace OptStorageManager
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 
@@ -3613,12 +3615,13 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->options = $14;
 					n->oncommit = $15;
 					n->tablespacename = $16;
+					n->storageManager = $17;
 					n->if_not_exists = true;
 					$$ = (Node *) n;
 				}
 		| CREATE OptTemp TABLE qualified_name OF any_name
 			OptTypedTableElementList OptPartitionSpec table_access_method_clause
-			OptWith OnCommitOption OptTableSpace
+			OptWith OnCommitOption OptTableSpace OptStorageManager
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 
@@ -3634,12 +3637,13 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->options = $10;
 					n->oncommit = $11;
 					n->tablespacename = $12;
+					n->storageManager = $13;
 					n->if_not_exists = false;
 					$$ = (Node *) n;
 				}
 		| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name
 			OptTypedTableElementList OptPartitionSpec table_access_method_clause
-			OptWith OnCommitOption OptTableSpace
+			OptWith OnCommitOption OptTableSpace OptStorageManager
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 
@@ -3655,12 +3659,13 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->options = $13;
 					n->oncommit = $14;
 					n->tablespacename = $15;
+					n->storageManager = $16;
 					n->if_not_exists = true;
 					$$ = (Node *) n;
 				}
 		| CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name
 			OptTypedTableElementList PartitionBoundSpec OptPartitionSpec
-			table_access_method_clause OptWith OnCommitOption OptTableSpace
+			table_access_method_clause OptWith OnCommitOption OptTableSpace OptStorageManager
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 
@@ -3676,12 +3681,13 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->options = $12;
 					n->oncommit = $13;
 					n->tablespacename = $14;
+					n->storageManager = $15;
 					n->if_not_exists = false;
 					$$ = (Node *) n;
 				}
 		| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF
 			qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec
-			table_access_method_clause OptWith OnCommitOption OptTableSpace
+			table_access_method_clause OptWith OnCommitOption OptTableSpace OptStorageManager
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 
@@ -3697,6 +3703,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->options = $15;
 					n->oncommit = $16;
 					n->tablespacename = $17;
+					n->storageManager = $18;
 					n->if_not_exists = true;
 					$$ = (Node *) n;
 				}
@@ -4513,6 +4520,11 @@ OptTableSpace:   TABLESPACE name					{ $$ = $2; }
 			| /*EMPTY*/								{ $$ = NULL; }
 		;
 
+OptStorageManager: STORAGE MANAGER name			    { $$ = $3; }
+			| /*EMPTY*/								{ $$ = NULL; }
+		;
+
+
 OptConsTableSpace:   USING INDEX TABLESPACE name	{ $$ = $4; }
 			| /*EMPTY*/								{ $$ = NULL; }
 		;
@@ -5827,6 +5839,24 @@ am_type:
 		|	TABLE			{ $$ = AMTYPE_TABLE; }
 		;
 
+
+/*****************************************************************************
+ *
+ *		QUERY:
+ *             CREATE STORAGE MANAGER name HANDLER handler_name
+ *
+ *****************************************************************************/
+
+CreateSmgrStmt: CREATE STORAGE MANAGER name HANDLER handler_name
+				{
+					CreateSmgrStmt *n = makeNode(CreateSmgrStmt);
+
+					n->smgrname = $4;
+					n->handler_name = $6;
+					$$ = (Node *) n;
+				}
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
@@ -6896,6 +6926,7 @@ drop_type_name:
 			| PUBLICATION							{ $$ = OBJECT_PUBLICATION; }
 			| SCHEMA								{ $$ = OBJECT_SCHEMA; }
 			| SERVER								{ $$ = OBJECT_FOREIGN_SERVER; }
+			| STORAGE MANAGER						{ $$ = OBJECT_STORAGE_MANAGER; }
 		;
 
 /* object types attached to a table */
@@ -17815,6 +17846,7 @@ unreserved_keyword:
 			| LOCK_P
 			| LOCKED
 			| LOGGED
+			| MANAGER
 			| MAPPING
 			| MATCH
 			| MATCHED
@@ -18422,6 +18454,7 @@ bare_label_keyword:
 			| LOCK_P
 			| LOCKED
 			| LOGGED
+			| MANAGER
 			| MAPPING
 			| MATCH
 			| MATCHED
diff --git a/src/backend/storage/smgr/Makefile b/src/backend/storage/smgr/Makefile
index 596b564656..929ee45435 100644
--- a/src/backend/storage/smgr/Makefile
+++ b/src/backend/storage/smgr/Makefile
@@ -14,6 +14,8 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = \
 	md.o \
-	smgr.o
+	smgr.o \
+	smgrapi.o \
+	smgr_handler.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index b3cf942d51..5f870e0309 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -23,27 +23,10 @@
 #include "storage/ipc.h"
 #include "storage/md.h"
 #include "storage/smgr.h"
+#include "catalog/pg_smgr.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
 
-static const f_smgr smgr_md = {
-	/* magnetic disk */
-		.smgr_init = mdinit,
-		.smgr_shutdown = NULL,
-		.smgr_open = mdopen,
-		.smgr_close = mdclose,
-		.smgr_create = mdcreate,
-		.smgr_exists = mdexists,
-		.smgr_unlink = mdunlink,
-		.smgr_extend = mdextend,
-		.smgr_prefetch = mdprefetch,
-		.smgr_read = mdread,
-		.smgr_write = mdwrite,
-		.smgr_writeback = mdwriteback,
-		.smgr_nblocks = mdnblocks,
-		.smgr_truncate = mdtruncate,
-		.smgr_immedsync = mdimmedsync,
-};
 
 /*
  * Each backend has a hashtable that stores all extant SMgrRelation objects.
@@ -68,11 +51,8 @@ static void smgrshutdown(int code, Datum arg);
 void
 smgrinit(void)
 {
-	if (smgr_init_hook)
-		(*smgr_init_hook)();
-
-	smgr_init_standard();
-
+	// init other smgrs in pg_init
+	mdinit();
 	/* register the shutdown proc */
 	on_proc_exit(smgrshutdown, 0);
 }
@@ -82,51 +62,8 @@ smgrinit(void)
  */
 static void
 smgrshutdown(int code, Datum arg)
-{
-	if (smgr_shutdown_hook)
-		(*smgr_shutdown_hook)();
-
-	smgr_shutdown_standard();
-}
-
-/* Hooks for plugins to get control in smgr */
-smgr_hook_type smgr_hook = NULL;
-smgr_init_hook_type smgr_init_hook = NULL;
-smgr_shutdown_hook_type smgr_shutdown_hook = NULL;
-
-const f_smgr *
-smgr_standard(BackendId backend, RelFileNode rnode)
-{
-	return &smgr_md;
-}
-
-void
-smgr_init_standard(void)
-{
-	mdinit();
-}
-
-void
-smgr_shutdown_standard(void)
 {
 }
-
-const f_smgr *
-smgr(BackendId backend, RelFileNode rnode)
-{
-	const f_smgr *result;
-
-	if (smgr_hook)
-	{
-		result = (*smgr_hook)(backend, rnode);
-	}
-	else
-		result = smgr_standard(backend, rnode);
-
-	return result;
-}
-
-
 /*
  *	smgropen() -- Return an SMgrRelation object, creating it if need be.
  *
@@ -167,14 +104,25 @@ smgropen(RelFileNode rnode, BackendId backend)
 		for (int i = 0; i <= MAX_FORKNUM; ++i)
 			reln->smgr_cached_nblocks[i] = InvalidBlockNumber;
 
-		reln->smgr = smgr(backend, rnode);
+		if (rnode.relSmgr == SMGR_MD_OID)
+		{
+			/*
+				* Avoid doing a syscache lookup for catalog tables.
+				*/
+			reln->smgr  = &smgr_md;
+		} else {
+			Assert(rnode.relSmgr != 0);
+			reln->smgr = GetTableSmgr(rnode.relSmgr);
+		}
+		Assert(reln->smgr != NULL);
 
 		/* implementation-specific initialization */
-		(*reln->smgr).smgr_open(reln);
+		reln->smgr->smgr_open(reln);
 
 		/* it has no owner yet */
 		dlist_push_tail(&unowned_relns, &reln->node);
 	}
+	Assert(reln->smgr != NULL);
 
 	return reln;
 }
diff --git a/src/backend/storage/smgr/smgr_handler.c b/src/backend/storage/smgr/smgr_handler.c
new file mode 100644
index 0000000000..4b99d5744f
--- /dev/null
+++ b/src/backend/storage/smgr/smgr_handler.c
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * smgr_md_handler.c
+ *	  md table access method code
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/storage/smgr/smgr_md_handler.c
+ *
+ *
+ * NOTES
+ *	  This files wires up the lower level heapam.c et al routines with the
+ *	  tableam abstraction.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "storage/smgr.h"
+#include "storage/md.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/heaptoast.h"
+#include "access/multixact.h"
+#include "access/rewriteheap.h"
+#include "access/syncscan.h"
+#include "access/tableam.h"
+#include "access/tsmapi.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/index.h"
+#include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
+#include "commands/progress.h"
+#include "executor/executor.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/lmgr.h"
+#include "storage/predicate.h"
+#include "storage/procarray.h"
+#include "storage/smgr.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+
+const TableSmgr smgr_md = {
+	/* magnetic disk */
+	.type = T_TableSmgr,
+	.smgr_init = mdinit,
+	.smgr_shutdown = NULL,
+	.smgr_open = mdopen,
+	.smgr_close = mdclose,
+	.smgr_create = mdcreate,
+	.smgr_exists = mdexists,
+	.smgr_unlink = mdunlink,
+	.smgr_extend = mdextend,
+	.smgr_prefetch = mdprefetch,
+	.smgr_read = mdread,
+	.smgr_write = mdwrite,
+	.smgr_writeback = mdwriteback,
+	.smgr_nblocks = mdnblocks,
+	.smgr_truncate = mdtruncate,
+	.smgr_immedsync = mdimmedsync,
+};
+
+Datum
+smgr_md_handler(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_POINTER(&smgr_md);
+}
diff --git a/src/backend/storage/smgr/smgrapi.c b/src/backend/storage/smgr/smgrapi.c
new file mode 100644
index 0000000000..dde65dc773
--- /dev/null
+++ b/src/backend/storage/smgr/smgrapi.c
@@ -0,0 +1,96 @@
+
+/*
+ * GetTableAmRoutine
+ *		Call the specified access method handler routine to get its
+ *		TableAmRoutine struct, which will be palloc'd in the caller's
+ *		memory context.
+ */
+
+#include "postgres.h"
+
+
+#include "storage/smgr.h"
+#include "catalog/pg_smgr.h"
+#include "catalog/pg_proc.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "utils/fmgroids.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+TableSmgr *
+GetTableSmgr(Oid smgrhandler)
+{
+	Datum		datum;
+	TableSmgr *routine;
+
+	datum = OidFunctionCall0(smgrhandler);
+	routine = (TableSmgr *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, TableSmgr))
+		elog(ERROR, "table storage manage handler %u did not return a f_smgr struct",
+			 smgrhandler);
+
+	/*
+	 * Assert that all required callbacks are present. That makes it a bit
+	 * easier to keep AMs up to date, e.g. when forward porting them to a new
+	 * major version.
+	 */
+	// Assert(routine->scan_begin != NULL);
+	// Assert(routine->scan_end != NULL);
+	// Assert(routine->scan_rescan != NULL);
+	// Assert(routine->scan_getnextslot != NULL);
+
+	// Assert(routine->parallelscan_estimate != NULL);
+	// Assert(routine->parallelscan_initialize != NULL);
+	// Assert(routine->parallelscan_reinitialize != NULL);
+
+	// Assert(routine->index_fetch_begin != NULL);
+	// Assert(routine->index_fetch_reset != NULL);
+	// Assert(routine->index_fetch_end != NULL);
+	// Assert(routine->index_fetch_tuple != NULL);
+
+	// Assert(routine->tuple_fetch_row_version != NULL);
+	// Assert(routine->tuple_tid_valid != NULL);
+	// Assert(routine->tuple_get_latest_tid != NULL);
+	// Assert(routine->tuple_satisfies_snapshot != NULL);
+	// Assert(routine->index_delete_tuples != NULL);
+
+	// Assert(routine->tuple_insert != NULL);
+
+	/*
+	 * Could be made optional, but would require throwing error during
+	 * parse-analysis.
+	//  */
+	// Assert(routine->tuple_insert_speculative != NULL);
+	// Assert(routine->tuple_complete_speculative != NULL);
+
+	// Assert(routine->multi_insert != NULL);
+	// Assert(routine->tuple_delete != NULL);
+	// Assert(routine->tuple_update != NULL);
+	// Assert(routine->tuple_lock != NULL);
+
+	// Assert(routine->relation_set_new_filenode != NULL);
+	// Assert(routine->relation_nontransactional_truncate != NULL);
+	// Assert(routine->relation_copy_data != NULL);
+	// Assert(routine->relation_copy_for_cluster != NULL);
+	// Assert(routine->relation_vacuum != NULL);
+	// Assert(routine->scan_analyze_next_block != NULL);
+	// Assert(routine->scan_analyze_next_tuple != NULL);
+	// Assert(routine->index_build_range_scan != NULL);
+	// Assert(routine->index_validate_scan != NULL);
+
+	// Assert(routine->relation_size != NULL);
+	// Assert(routine->relation_needs_toast_table != NULL);
+
+	// Assert(routine->relation_estimate_size != NULL);
+
+	// /* optional, but one callback implies presence of the other */
+	// Assert((routine->scan_bitmap_next_block == NULL) ==
+	// 	   (routine->scan_bitmap_next_tuple == NULL));
+	// Assert(routine->scan_sample_next_block != NULL);
+	// Assert(routine->scan_sample_next_tuple != NULL);
+
+	return routine;
+}
\ No newline at end of file
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6a5bcded55..bb91383a03 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -192,6 +192,7 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
 		case T_CreateSeqStmt:
 		case T_CreateStatsStmt:
 		case T_CreateStmt:
+		case T_CreateSmgrStmt:
 		case T_CreateSubscriptionStmt:
 		case T_CreateTableAsStmt:
 		case T_CreateTableSpaceStmt:
@@ -1903,6 +1904,10 @@ ProcessUtilitySlow(ParseState *pstate,
 				address = AlterCollation((AlterCollationStmt *) parsetree);
 				break;
 
+
+			case T_CreateSmgrStmt:
+				address = CreateStorageManager((CreateSmgrStmt *) parsetree);
+				break;
 			default:
 				elog(ERROR, "unrecognized node type: %d",
 					 (int) nodeTag(parsetree));
@@ -2640,6 +2645,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_STATISTIC_EXT:
 					tag = CMDTAG_DROP_STATISTICS;
 					break;
+				case OBJECT_STORAGE_MANAGER:
+					tag = CMDTAG_DROP_STORAGE_MANAGER;
+					break;
 				default:
 					tag = CMDTAG_UNKNOWN;
 			}
@@ -3075,6 +3083,10 @@ CreateCommandTag(Node *parsetree)
 			tag = CMDTAG_CREATE_STATISTICS;
 			break;
 
+		case T_CreateSmgrStmt:
+			tag = CMDTAG_CREATE_STORAGE_MANAGER;
+			break;
+
 		case T_AlterStatsStmt:
 			tag = CMDTAG_ALTER_STATISTICS;
 			break;
@@ -3656,6 +3668,9 @@ GetCommandLogLevel(Node *parsetree)
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_CreateSmgrStmt:
+			lev = LOGSTMT_DDL;
+			break;
 		case T_CreatePublicationStmt:
 			lev = LOGSTMT_DDL;
 			break;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index c8202502c9..01ef244a80 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -389,3 +389,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(table_smgr_handler);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 60e72f9e8b..6038a975ef 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -59,6 +59,7 @@
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_shseclabel.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
@@ -77,6 +78,7 @@
 #include "rewrite/rowsecurity.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
+#include "storage/md.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
@@ -1209,6 +1211,8 @@ retry:
 	else
 		Assert(relation->rd_rel->relam == InvalidOid);
 
+	RelationInitSmgrHandler(relation);
+
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
 
@@ -1843,6 +1847,51 @@ RelationInitTableAccessMethod(Relation relation)
 	InitTableAmRoutine(relation);
 }
 
+
+static void
+InitTableSmgr(Relation relation)
+{
+	if (IsCatalogRelation(relation))
+	{
+		/*
+		 * Avoid doing a syscache lookup for catalog tables.
+		 */
+		Assert(relation->rd_rel->relsmgr == SMGR_MD_OID);
+		relation->rd_f_smgr = &smgr_md;
+	} else {
+		relation->rd_f_smgr = GetTableSmgr(relation->rd_smgrhandler);
+	}
+}
+
+void RelationInitSmgrHandler(Relation relation) {
+	HeapTuple	tuple;
+	Form_pg_smgr smgrform;
+
+	/*
+		* Look up the table storage manager, save the OID of its handler
+		* function.
+		*/
+	
+	if (IsCatalogRelation(relation)) {
+		relation->rd_smgrhandler = F_SMGR_MD_HANDLER;
+		relation->rd_rel->relsmgr = SMGR_MD_OID;
+	} else {
+		tuple = SearchSysCache1(SMGROID,
+								ObjectIdGetDatum(relation->rd_rel->relsmgr));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for storage manager %u"	,
+					relation->rd_rel->relsmgr);
+		smgrform = (Form_pg_smgr) GETSTRUCT(tuple);
+		relation->rd_smgrhandler = smgrform->smgrhandler;
+		ReleaseSysCache(tuple);
+	}
+
+	Assert(relation->rd_rel->relsmgr != InvalidOid);
+	relation->rd_node.relSmgr = relation->rd_smgrhandler;
+	InitTableSmgr(relation);
+}
+
+
 /*
  *		formrdesc
  *
@@ -1931,6 +1980,8 @@ formrdesc(const char *relationName, Oid relationReltype,
 	relation->rd_rel->relkind = RELKIND_RELATION;
 	relation->rd_rel->relnatts = (int16) natts;
 	relation->rd_rel->relam = HEAP_TABLE_AM_OID;
+	relation->rd_rel->relsmgr = SMGR_MD_OID;
+	relation->rd_node.relSmgr = F_SMGR_MD_HANDLER;
 
 	/*
 	 * initialize attribute tuple form
@@ -1983,6 +2034,7 @@ formrdesc(const char *relationName, Oid relationReltype,
 	 * specifying that the initial filenode is the same as the OID.
 	 */
 	relation->rd_rel->relfilenode = InvalidOid;
+	relation->rd_node.relSmgr = F_SMGR_MD_HANDLER;
 	if (IsBootstrapProcessingMode())
 		RelationMapUpdateMap(RelationGetRelid(relation),
 							 RelationGetRelid(relation),
@@ -3459,6 +3511,7 @@ RelationBuildLocalRelation(const char *relname,
 						   TupleDesc tupDesc,
 						   Oid relid,
 						   Oid accessmtd,
+						   Oid smgrid,
 						   Oid relfilenode,
 						   Oid reltablespace,
 						   bool shared_relation,
@@ -3642,6 +3695,7 @@ RelationBuildLocalRelation(const char *relname,
 	RelationInitPhysicalAddr(rel);
 
 	rel->rd_rel->relam = accessmtd;
+	rel->rd_rel->relsmgr = smgrid;
 
 	/*
 	 * RelationInitTableAccessMethod will do syscache lookups, so we mustn't
@@ -3653,6 +3707,9 @@ RelationBuildLocalRelation(const char *relname,
 	if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_SEQUENCE)
 		RelationInitTableAccessMethod(rel);
 
+	rel->rd_rel->relsmgr = smgrid;
+	RelationInitSmgrHandler(rel);
+
 	/*
 	 * Okay to insert into the relcache hash table.
 	 *
@@ -3709,7 +3766,7 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 
 	/* Allocate a new relfilenode */
 	newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
-									   persistence);
+									   persistence, relation->rd_rel->relsmgr);
 
 	/*
 	 * Get a writable copy of the pg_class tuple for the given relation.
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 1912b12146..7c8de8e2db 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -64,6 +64,7 @@
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_statistic_ext.h"
 #include "catalog/pg_statistic_ext_data.h"
+#include "catalog/pg_smgr.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_subscription_rel.h"
 #include "catalog/pg_tablespace.h"
@@ -829,6 +830,28 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		128
 	},
+	{StorageManagerRelationId,		/* STORANGE MANAGER OID */
+		SmgrOidIndexId,
+		1,
+		{
+			Anum_pg_smgr_oid,
+			0,
+			0,
+			0,
+		},
+		4
+	},
+	{StorageManagerRelationId,		/* STORANGE MANAGER NAME*/
+		SmgrNameIndexId,
+		1,
+		{
+			Anum_pg_smgr_smgrname,
+			0,
+			0,
+			0,
+		},
+		4
+	},
 	{SubscriptionRelationId,	/* SUBSCRIPTIONNAME */
 		SubscriptionNameIndexId,
 		2,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 1a5d924a23..5e5cb9f1f7 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1534,6 +1534,7 @@ describeOneTableDetails(const char *schemaname,
 		char		relpersistence;
 		char		relreplident;
 		char	   *relam;
+		char       *relsmgr;
 	}			tableinfo;
 	bool		show_column_details = false;
 
diff --git a/src/include/catalog/catalog.h b/src/include/catalog/catalog.h
index 60c1215362..afdc7fe0c7 100644
--- a/src/include/catalog/catalog.h
+++ b/src/include/catalog/catalog.h
@@ -39,6 +39,6 @@ extern bool IsPinnedObject(Oid classId, Oid objectId);
 extern Oid	GetNewOidWithIndex(Relation relation, Oid indexId,
 							   AttrNumber oidcolumn);
 extern Oid	GetNewRelFileNode(Oid reltablespace, Relation pg_class,
-							  char relpersistence);
+							  char relpersistence, Oid relsmgr);
 
 #endif							/* CATALOG_H */
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index d027075a4c..d57459226a 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -126,6 +126,7 @@ typedef enum ObjectClass
 	OCLASS_PUBLICATION_NAMESPACE,	/* pg_publication_namespace */
 	OCLASS_PUBLICATION_REL,		/* pg_publication_rel */
 	OCLASS_SUBSCRIPTION,		/* pg_subscription */
+	OCLASS_SMGR,				/* pg_smgr */
 	OCLASS_TRANSFORM			/* pg_transform */
 } ObjectClass;
 
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 07c5b88f0e..41e7e84fda 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -52,6 +52,7 @@ extern Relation heap_create(const char *relname,
 							Oid relid,
 							Oid relfilenode,
 							Oid accessmtd,
+							Oid smgrid,
 							TupleDesc tupDesc,
 							char relkind,
 							char relpersistence,
@@ -70,6 +71,7 @@ extern Oid	heap_create_with_catalog(const char *relname,
 									 Oid reloftypeid,
 									 Oid ownerid,
 									 Oid accessmtd,
+									 Oid smgrid,
 									 TupleDesc tupdesc,
 									 List *cooked_constraints,
 									 char relkind,
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e1f4eefa22..a6a669e655 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -52,6 +52,9 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat
 	/* access method; 0 if not a table / index */
 	Oid			relam BKI_DEFAULT(heap) BKI_LOOKUP_OPT(pg_am);
 
+	/* stogare manager; */
+	Oid			relsmgr BKI_DEFAULT(4646);
+
 	/* identifier of physical storage file */
 	/* relfilenode == 0 means it is a "mapped" relation, see relmapper.c */
 	Oid			relfilenode BKI_DEFAULT(0);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87aa571a33..c2469e6fc2 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7443,6 +7443,13 @@
 { oid => '268', descr => 'I/O',
   proname => 'table_am_handler_out', prorettype => 'cstring',
   proargtypes => 'table_am_handler', prosrc => 'table_am_handler_out' },
+{ oid => '4648', descr => 'I/O',
+  proname => 'table_smgr_handler_in', proisstrict => 'f',
+  prorettype => 'table_smgr_handler', proargtypes => 'cstring',
+  prosrc => 'table_smgr_handler_in' },
+{ oid => '4649', descr => 'I/O',
+  proname => 'table_smgr_handler_out', prorettype => 'cstring',
+  proargtypes => 'table_smgr_handler', prosrc => 'table_smgr_handler_out' },
 { oid => '5086', descr => 'I/O',
   proname => 'anycompatible_in', prorettype => 'anycompatible',
   proargtypes => 'cstring', prosrc => 'anycompatible_in' },
@@ -11885,4 +11892,10 @@
   prorettype => 'bytea', proargtypes => 'pg_brin_minmax_multi_summary',
   prosrc => 'brin_minmax_multi_summary_send' },
 
+# Storage access method handlers
+{ oid => '4642', descr => 'md storage manager handler',
+  proname => 'smgr_md_handler', provolatile => 'v',
+  prorettype => 'table_smgr_handler', proargtypes => 'internal',
+  prosrc => 'smgr_md_handler' },
+
 ]
diff --git a/src/include/catalog/pg_smgr.dat b/src/include/catalog/pg_smgr.dat
new file mode 100644
index 0000000000..ee7dea1087
--- /dev/null
+++ b/src/include/catalog/pg_smgr.dat
@@ -0,0 +1,17 @@
+#----------------------------------------------------------------------
+#
+# pg_smgr.dat
+#    Initial contents of the pg_smgr system catalog.
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/include/catalog/pg_smgr.dat
+#
+#----------------------------------------------------------------------
+
+[
+{ oid => '4646', oid_symbol => 'SMGR_MD_OID',
+  descr => 'md storage manager',
+  smgrname => 'md', smgrhandler => 'smgr_md_handler' },
+]
diff --git a/src/include/catalog/pg_smgr.h b/src/include/catalog/pg_smgr.h
new file mode 100644
index 0000000000..c2152cd844
--- /dev/null
+++ b/src/include/catalog/pg_smgr.h
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_smgr.h
+ *	  definition of the "storage manager" system catalog (pg_smgr)
+ *
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_smgr.h
+ *
+ * NOTES
+ *	  The Catalog.pm module reads this file and derives schema
+ *	  information.
+ *
+ *-------------------------------------------------------------------------
+ */
+/* ----------------
+ *		pg_smgr definition.  cpp turns this into
+ *		typedef struct FormData_pg_smgr
+ * ----------------
+ */
+
+
+#ifndef PG_SMGR_H
+#define PG_SMGR_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_smgr_d.h"
+
+CATALOG(pg_smgr,4643,StorageManagerRelationId)
+{
+	Oid			oid;			/* oid */
+
+	/* storage manager name */
+	NameData	smgrname;
+
+	/* handler function */
+	regproc		smgrhandler BKI_LOOKUP(pg_proc);
+} FormData_pg_smgr;
+
+/* ----------------
+ *		Form_pg_smgr corresponds to a pointer to a tuple with
+ *		the format of pg_smgr relation.
+ * ----------------
+ */
+typedef FormData_pg_smgr *Form_pg_smgr;
+
+DECLARE_UNIQUE_INDEX(pg_smgr_name_index, 4644, SmgrNameIndexId, on pg_smgr using btree(smgrname name_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_smgr_oid_index, 4645, SmgrOidIndexId, on pg_smgr using btree(oid oid_ops));
+
+#endif              /* PG_SMGR_H */
\ No newline at end of file
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index df45879463..446c3f1c55 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -637,6 +637,11 @@
   typcategory => 'P', typinput => 'table_am_handler_in',
   typoutput => 'table_am_handler_out', typreceive => '-', typsend => '-',
   typalign => 'i' },
+{ oid => '4647',
+  typname => 'table_smgr_handler', typlen => '4', typbyval => 't', typtype => 'p',
+  typcategory => 'P', typinput => 'table_smgr_handler_in',
+  typoutput => 'table_smgr_handler_out', typreceive => '-', typsend => '-',
+  typalign => 'i' },
 { oid => '3831',
   descr => 'pseudo-type representing a range over a polymorphic base type',
   typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index df8e73af40..64535486a2 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -37,7 +37,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
 									   LOCKMODE lockmode);
 extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);
 
-extern Oid	make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
+extern Oid	make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, Oid NewStorageMethod,
 						  char relpersistence, LOCKMODE lockmode);
 extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 							 bool is_system_catalog,
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 56d2bb6616..99b27164dd 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -143,6 +143,11 @@ extern Oid	get_table_am_oid(const char *amname, bool missing_ok);
 extern Oid	get_am_oid(const char *amname, bool missing_ok);
 extern char *get_am_name(Oid amOid);
 
+/* commands/smgrcmds.c */
+extern Oid get_smgr_oid(const char *smgrname, bool missing_ok);
+extern ObjectAddress CreateStorageManager(CreateSmgrStmt *stmt);
+extern char *get_smgr_name(Oid amOid);
+
 /* support routines in commands/define.c */
 
 extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index b3b407579b..0622739a79 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -437,6 +437,7 @@ typedef enum NodeTag
 	T_AlterPolicyStmt,
 	T_CreateTransformStmt,
 	T_CreateAmStmt,
+	T_CreateSmgrStmt,
 	T_CreatePublicationStmt,
 	T_AlterPublicationStmt,
 	T_CreateSubscriptionStmt,
@@ -553,6 +554,7 @@ typedef enum NodeTag
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
 	T_TableAmRoutine,			/* in access/tableam.h */
+	T_TableSmgr,				/* in storage/smgr.h */
 	T_TsmRoutine,				/* in access/tsmapi.h */
 	T_ForeignKeyCacheInfo,		/* in utils/rel.h */
 	T_CallContext,				/* in nodes/parsenodes.h */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 73f635b455..ac1f49422f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2172,6 +2172,7 @@ typedef enum ObjectType
 	OBJECT_SEQUENCE,
 	OBJECT_SUBSCRIPTION,
 	OBJECT_STATISTIC_EXT,
+	OBJECT_STORAGE_MANAGER,
 	OBJECT_TABCONSTRAINT,
 	OBJECT_TABLE,
 	OBJECT_TABLESPACE,
@@ -2540,6 +2541,7 @@ typedef struct CreateStmt
 	OnCommitAction oncommit;	/* what do we do at COMMIT? */
 	char	   *tablespacename; /* table space to use, or NULL */
 	char	   *accessMethod;	/* table access method */
+	char	   *storageManager;	/* table access method */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 } CreateStmt;
 
@@ -2881,6 +2883,18 @@ typedef struct CreateAmStmt
 	char		amtype;			/* type of access method */
 } CreateAmStmt;
 
+
+/*----------------------
+ *		Create STORAGE MANAGER Statement
+ *----------------------
+ */
+typedef struct CreateSmgrStmt
+{
+	NodeTag		type;
+	char	   *smgrname;			/* access method name */
+	List	   *handler_name;	/* handler function name */
+} CreateSmgrStmt;
+
 /* ----------------------
  *		Create TRIGGER Statement
  * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index ae35f03251..0fa6186f90 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -267,6 +267,7 @@ PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("manager", MANAGER, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("matched", MATCHED, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/storage/md.h b/src/include/storage/md.h
index ffffa40db7..f3b6f5942c 100644
--- a/src/include/storage/md.h
+++ b/src/include/storage/md.h
@@ -49,4 +49,7 @@ extern int	mdsyncfiletag(const FileTag *ftag, char *path);
 extern int	mdunlinkfiletag(const FileTag *ftag, char *path);
 extern bool mdfiletagmatches(const FileTag *ftag, const FileTag *candidate);
 
+
+const TableSmgr smgr_md;
+
 #endif							/* MD_H */
diff --git a/src/include/storage/relfilenode.h b/src/include/storage/relfilenode.h
index 4fdc606cc3..e1d51ebc6e 100644
--- a/src/include/storage/relfilenode.h
+++ b/src/include/storage/relfilenode.h
@@ -59,6 +59,7 @@ typedef struct RelFileNode
 	Oid			spcNode;		/* tablespace */
 	Oid			dbNode;			/* database */
 	Oid			relNode;		/* relation */
+	Oid         relSmgr;         /* smgr handler */
 } RelFileNode;
 
 /*
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 7aca0d5ca5..30778c23df 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -14,9 +14,13 @@
 #ifndef SMGR_H
 #define SMGR_H
 
+#include "postgres.h"
+
 #include "lib/ilist.h"
 #include "storage/block.h"
 #include "storage/relfilenode.h"
+#include "utils/snapshot.h"
+#include "nodes/nodes.h"
 
 struct f_smgr;
 
@@ -61,8 +65,11 @@ typedef struct SMgrRelationData
 	 * Fields below here are intended to be private to smgr.c and its
 	 * submodules.  Do not touch them from elsewhere.
 	 */
-	const struct f_smgr *smgr; /* storage manager selector */
 
+	/*
+	 * Storage manager.
+	 */
+	const struct TableSmgr *smgr;
 	/*
 	 * for md.c; per-fork arrays of the number of open segments
 	 * (md_num_open_segs) and the segments themselves (md_seg_fds).
@@ -90,8 +97,12 @@ typedef SMgrRelationData *SMgrRelation;
  * would normally be errors should be allowed during bootstrap and/or WAL
  * recovery --- see comments in md.c for details.
  */
-typedef struct f_smgr
+typedef struct TableSmgr
 {
+	/* this must be set to T_TableSmgr */
+	NodeTag		type;
+
+
 	void		(*smgr_init) (void);	/* may be NULL */
 	void		(*smgr_shutdown) (void);	/* may be NULL */
 	void		(*smgr_open) (SMgrRelation reln);
@@ -115,21 +126,7 @@ typedef struct f_smgr
 	void		(*smgr_truncate) (SMgrRelation reln, ForkNumber forknum,
 								  BlockNumber nblocks);
 	void		(*smgr_immedsync) (SMgrRelation reln, ForkNumber forknum);
-} f_smgr;
-
-typedef void (*smgr_init_hook_type) (void);
-typedef void (*smgr_shutdown_hook_type) (void);
-extern PGDLLIMPORT smgr_init_hook_type smgr_init_hook;
-extern PGDLLIMPORT smgr_shutdown_hook_type smgr_shutdown_hook;
-extern void smgr_init_standard(void);
-extern void smgr_shutdown_standard(void);
-
-
-typedef const f_smgr *(*smgr_hook_type) (BackendId backend, RelFileNode rnode);
-extern PGDLLIMPORT smgr_hook_type smgr_hook;
-extern const f_smgr *smgr_standard(BackendId backend, RelFileNode rnode);
-
-extern const f_smgr *smgr(BackendId backend, RelFileNode rnode);
+} TableSmgr;
 
 extern void smgrinit(void);
 extern SMgrRelation smgropen(RelFileNode rnode, BackendId backend);
@@ -162,4 +159,8 @@ extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum);
 extern void AtEOXact_SMgr(void);
 extern bool ProcessBarrierSmgrRelease(void);
 
+
+TableSmgr *
+GetTableSmgr(Oid smgrhandler);
+
 #endif							/* SMGR_H */
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index 2b1163ce33..28dff1f8ad 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -111,6 +111,7 @@ PG_CMDTAG(CMDTAG_CREATE_SCHEMA, "CREATE SCHEMA", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_SEQUENCE, "CREATE SEQUENCE", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_SERVER, "CREATE SERVER", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_STATISTICS, "CREATE STATISTICS", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_STORAGE_MANAGER, "CREATE STORAGE MANAGER", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_SUBSCRIPTION, "CREATE SUBSCRIPTION", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_TABLE, "CREATE TABLE", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_TABLE_AS, "CREATE TABLE AS", true, false, false)
@@ -164,6 +165,7 @@ PG_CMDTAG(CMDTAG_DROP_SCHEMA, "DROP SCHEMA", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_SEQUENCE, "DROP SEQUENCE", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_SERVER, "DROP SERVER", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_STATISTICS, "DROP STATISTICS", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_STORAGE_MANAGER, "DROP STORAGE MANAGER", false, false, false)
 PG_CMDTAG(CMDTAG_DROP_SUBSCRIPTION, "DROP SUBSCRIPTION", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_TABLE, "DROP TABLE", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_TABLESPACE, "DROP TABLESPACE", false, false, false)
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 90b3c49bc1..7fa646fe0a 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -184,6 +184,13 @@ typedef struct RelationData
 	 */
 	const struct TableAmRoutine *rd_tableam;
 
+
+	Oid			rd_smgrhandler;	/* OID of index smgr handler function */
+	/*
+	 * Storage manager.
+	 */
+	const struct TableSmgr *rd_f_smgr;
+
 	/* These are non-NULL only for an index relation: */
 	Form_pg_index rd_index;		/* pg_index tuple describing this index */
 	/* use "struct" here to avoid needing to include htup.h: */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 86dddbd975..2c95debeb7 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -79,6 +79,7 @@ extern void RelationBuildPublicationDesc(Relation relation,
 										 struct PublicationDesc *pubdesc);
 
 extern void RelationInitTableAccessMethod(Relation relation);
+extern void RelationInitSmgrHandler(Relation relation);
 
 /*
  * Routines to support ereport() reports of relation-related errors
@@ -103,6 +104,7 @@ extern Relation RelationBuildLocalRelation(const char *relname,
 										   TupleDesc tupDesc,
 										   Oid relid,
 										   Oid accessmtd,
+										   Oid smgrid,
 										   Oid relfilenode,
 										   Oid reltablespace,
 										   bool shared_relation,
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4463ea66be..8ac7e10760 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -95,6 +95,8 @@ enum SysCacheIdentifier
 	STATEXTNAMENSP,
 	STATEXTOID,
 	STATRELATTINH,
+	SMGROID,
+	SMGRNAME,
 	SUBSCRIPTIONNAME,
 	SUBSCRIPTIONOID,
 	SUBSCRIPTIONRELMAP,
diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c
index 7ef272cc7a..44f8fd3131 100644
--- a/src/test/modules/test_oat_hooks/test_oat_hooks.c
+++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c
@@ -1485,6 +1485,9 @@ nodetag_to_string(NodeTag tag)
 		case T_CreateStatsStmt:
 			return "CreateStatsStmt";
 			break;
+		case T_CreateSmgrStmt:
+			return "CreateSmgrStmt";
+			break;
 		case T_AlterCollationStmt:
 			return "AlterCollationStmt";
 			break;
@@ -1713,6 +1716,9 @@ nodetag_to_string(NodeTag tag)
 		case T_TableAmRoutine:
 			return "TableAmRoutine";
 			break;
+		case T_TableSmgr:
+			return "TableSmgr";
+			break;
 		case T_TsmRoutine:
 			return "TsmRoutine";
 			break;
-- 
2.25.1

