v14-0004-Create-custom-compression-methods.patch

application/octet-stream

Filename: v14-0004-Create-custom-compression-methods.patch
Type: application/octet-stream
Part: 4
Message: Re: [HACKERS] Custom compression methods

Patch

Format: format-patch
Series: patch v14-0004
Subject: Create custom compression methods
File+
doc/src/sgml/ref/allfiles.sgml 3 1
doc/src/sgml/ref/create_compression_method.sgml 110 0
doc/src/sgml/ref/drop_compression_method.sgml 110 0
doc/src/sgml/reference.sgml 2 0
src/backend/access/common/detoast.c 47 6
src/backend/access/common/toast_internals.c 11 4
src/backend/access/compression/compressionapi.c 1 1
src/backend/access/compression/lz4.c 12 11
src/backend/access/compression/pglz.c 10 10
src/backend/catalog/aclchk.c 2 0
src/backend/catalog/dependency.c 1 1
src/backend/catalog/objectaddress.c 22 0
src/backend/commands/compressioncmds.c 104 0
src/backend/commands/event_trigger.c 3 0
src/backend/commands/seclabel.c 1 0
src/backend/executor/nodeModifyTable.c 1 2
src/backend/nodes/copyfuncs.c 14 0
src/backend/nodes/equalfuncs.c 12 0
src/backend/parser/gram.y 19 1
src/backend/tcop/utility.c 16 0
src/include/access/compressionapi.h 16 8
src/include/access/detoast.h 8 0
src/include/access/toast_internals.h 18 1
src/include/commands/defrem.h 2 0
src/include/nodes/nodes.h 2 1
src/include/nodes/parsenodes.h 12 0
src/include/postgres.h 8 0
src/include/tcop/cmdtaglist.h 2 0
src/test/regress/expected/create_cm.out 18 0
src/test/regress/sql/create_cm.sql 7 0
From 93fea204914175edbae05fc4cbdcb817cd788b70 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Tue, 20 Oct 2020 17:57:00 +0530
Subject: [PATCH v14 4/6] Create custom compression methods

Provide syntax to create custom compression methods.
---
 doc/src/sgml/ref/allfiles.sgml                |   4 +-
 .../sgml/ref/create_compression_method.sgml   | 110 ++++++++++++++++++
 doc/src/sgml/ref/drop_compression_method.sgml | 110 ++++++++++++++++++
 doc/src/sgml/reference.sgml                   |   2 +
 src/backend/access/common/detoast.c           |  53 ++++++++-
 src/backend/access/common/toast_internals.c   |  15 ++-
 .../access/compression/compressionapi.c       |   2 +-
 src/backend/access/compression/lz4.c          |  23 ++--
 src/backend/access/compression/pglz.c         |  20 ++--
 src/backend/catalog/aclchk.c                  |   2 +
 src/backend/catalog/dependency.c              |   2 +-
 src/backend/catalog/objectaddress.c           |  22 ++++
 src/backend/commands/compressioncmds.c        | 104 +++++++++++++++++
 src/backend/commands/event_trigger.c          |   3 +
 src/backend/commands/seclabel.c               |   1 +
 src/backend/executor/nodeModifyTable.c        |   3 +-
 src/backend/nodes/copyfuncs.c                 |  14 +++
 src/backend/nodes/equalfuncs.c                |  12 ++
 src/backend/parser/gram.y                     |  20 +++-
 src/backend/tcop/utility.c                    |  16 +++
 src/include/access/compressionapi.h           |  24 ++--
 src/include/access/detoast.h                  |   8 ++
 src/include/access/toast_internals.h          |  19 ++-
 src/include/commands/defrem.h                 |   2 +
 src/include/nodes/nodes.h                     |   3 +-
 src/include/nodes/parsenodes.h                |  12 ++
 src/include/postgres.h                        |   8 ++
 src/include/tcop/cmdtaglist.h                 |   2 +
 src/test/regress/expected/create_cm.out       |  18 +++
 src/test/regress/sql/create_cm.sql            |   7 ++
 30 files changed, 594 insertions(+), 47 deletions(-)
 create mode 100644 doc/src/sgml/ref/create_compression_method.sgml
 create mode 100644 doc/src/sgml/ref/drop_compression_method.sgml

diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 0f0064150c..354ebb4b75 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -62,6 +62,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
 <!ENTITY createCast         SYSTEM "create_cast.sgml">
 <!ENTITY createCollation    SYSTEM "create_collation.sgml">
+<!ENTITY createCompressionMethod SYSTEM "create_compression_method.sgml">
 <!ENTITY createConversion   SYSTEM "create_conversion.sgml">
 <!ENTITY createDatabase     SYSTEM "create_database.sgml">
 <!ENTITY createDomain       SYSTEM "create_domain.sgml">
@@ -105,10 +106,11 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY delete             SYSTEM "delete.sgml">
 <!ENTITY discard            SYSTEM "discard.sgml">
 <!ENTITY do                 SYSTEM "do.sgml">
-<!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
+<!ENTITY dropCompressionMethod   SYSTEM "drop_access_method.sgml">
 <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
 <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
 <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
+<!ENTITY dropAccessMethod   SYSTEM "drop_compression_method.sgml">
 <!ENTITY dropConversion     SYSTEM "drop_conversion.sgml">
 <!ENTITY dropDatabase       SYSTEM "drop_database.sgml">
 <!ENTITY dropDomain         SYSTEM "drop_domain.sgml">
diff --git a/doc/src/sgml/ref/create_compression_method.sgml b/doc/src/sgml/ref/create_compression_method.sgml
new file mode 100644
index 0000000000..560a7f2942
--- /dev/null
+++ b/doc/src/sgml/ref/create_compression_method.sgml
@@ -0,0 +1,110 @@
+<!--
+doc/src/sgml/ref/create_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-create-compression-method">
+ <indexterm zone="sql-create-compression-method">
+  <primary>CREATE COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>CREATE COMPRESSION METHOD</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE COMPRESSION METHOD</refname>
+  <refpurpose>define a new compresion method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE COMPRESSION METHOD <replaceable class="parameter">name</replaceable>
+    HANDLER <replaceable class="parameter">handler_function</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE COMPRESSION METHOD</command> creates a new compression method.
+  </para>
+
+  <para>
+   The compression method name must be unique within the database.
+  </para>
+
+  <para>
+   Only superusers can define new compression methods.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the compression method to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">handler_function</replaceable></term>
+    <listitem>
+     <para>
+      <replaceable class="parameter">handler_function</replaceable> is the
+      name (possibly schema-qualified) of a previously registered function
+      that represents the compression method. The handler function must be declared
+      to accept a single argument of type <type>internal</type> and to return
+      the pseudo-type <type>compression_handler</type>. The argument is a
+      dummy value that simply serves to prevent handler functions from being
+      called directly from SQL commands. The result of the function must be a
+      palloc'd struct of type <structname>CompressionRoutine</structname>,
+      which contains everything that the core code needs to know to make use of
+      the compression method.
+      The <structname>CompressionRoutine</structname> struct, also called the
+      compression method's <firstterm>API struct</firstterm>, contains pointers to
+      support functions for the compression method. These support functions are
+      plain C functions and are not visible or callable at the SQL level.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Create an compression method <literal>pglz2</literal> with
+   handler function <literal>pglzhandler</literal>:
+<programlisting>
+CREATE COMPRESSION METHOD pglz2 HANDLER pglzhandler;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE COMPRESSION METHOD</command> is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-drop-compression-method"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/drop_compression_method.sgml b/doc/src/sgml/ref/drop_compression_method.sgml
new file mode 100644
index 0000000000..2a7399f6c2
--- /dev/null
+++ b/doc/src/sgml/ref/drop_compression_method.sgml
@@ -0,0 +1,110 @@
+<!--
+doc/src/sgml/ref/drop_compression_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-drop-compression-method">
+ <indexterm zone="sql-drop-compression-method">
+  <primary>DROP COMPRESSION METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>DROP COMPRESSION METHOD</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP COMPRESSION METHOD</refname>
+  <refpurpose>remove a compression method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP COMPRESSION METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP COMPRESSION METHOD</command> removes an existing compression method.
+   Only superusers can drop compression methods.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the compression method does not exist.
+      A notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing compression method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the compression method
+      and in turn all objects that depend on those objects
+      (see <xref linkend="ddl-depend"/>).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the compression method if any objects depend on it.
+      This is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Drop the compression method <literal>pglz2</literal>:
+<programlisting>
+DROP COMPRESSION METHOD pglz2;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>DROP COMPRESSION METHOD</command> is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-create-compression-method"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index dd2bddab8c..381f52a359 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -90,6 +90,7 @@
    &createAggregate;
    &createCast;
    &createCollation;
+   &createCompressionMethod;
    &createConversion;
    &createDatabase;
    &createDomain;
@@ -137,6 +138,7 @@
    &dropAggregate;
    &dropCast;
    &dropCollation;
+   &dropCompressionMethod;
    &dropConversion;
    &dropDatabase;
    &dropDomain;
diff --git a/src/backend/access/common/detoast.c b/src/backend/access/common/detoast.c
index 9fa4e2da1d..174e6f6d5c 100644
--- a/src/backend/access/common/detoast.c
+++ b/src/backend/access/common/detoast.c
@@ -438,6 +438,37 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
 	return result;
 }
 
+/* ----------
+ * toast_get_compression_oid -
+ *
+ * Return the Oid of the compresion method stored in the compressed data.
+ * For built-in methods, we only store the built-in compression method id in
+ * first 2-bits of the rawsize and that is directly mapped to the compression
+ * method Oid.  And, for the custom compression method we store the Oid of the
+ * compression method in the custom compression header.
+ */
+Oid
+toast_get_compression_oid(struct varlena *attr)
+{
+	CompressionId cmid;
+
+	/*
+	 * If it is custom compression id then get the Oid from the custom
+	 * compression header otherwise, directly translate the buil-in compression
+	 * id to compression method Oid.
+	 */
+	cmid = TOAST_COMPRESS_METHOD(attr);
+	if (IsCustomCompression(cmid))
+	{
+		toast_compress_header_custom *hdr;
+
+		hdr = (toast_compress_header_custom *) attr;
+		return hdr->cmoid;
+	}
+	else
+		return GetCompressionOidFromCompressionId(cmid);
+}
+
 /* ----------
  * toast_decompress_datum -
  *
@@ -451,12 +482,16 @@ toast_decompress_datum(struct varlena *attr)
 
 	Assert(VARATT_IS_COMPRESSED(attr));
 
-	cmoid = GetCompressionOidFromCompressionId(TOAST_COMPRESS_METHOD(attr));
+	/* get the compression method oid */
+	cmoid = toast_get_compression_oid(attr);
 
 	/* get compression method handler routines */
 	cmroutine = GetCompressionRoutine(cmoid);
 
-	return cmroutine->cmdecompress(attr);
+	/* call the decompression routine */
+	return cmroutine->cmdecompress(attr,
+								   IsCustomCompression(GetCompressionId(cmoid)) ?
+								   TOAST_CUSTOM_COMPRESS_HDRSZ : TOAST_COMPRESS_HDRSZ);
 }
 
 
@@ -470,24 +505,30 @@ toast_decompress_datum(struct varlena *attr)
 static struct varlena *
 toast_decompress_datum_slice(struct varlena *attr, int32 slicelength)
 {
+	Oid					cmoid;
+	int32				header_sz;
 	CompressionRoutine *cmroutine;
-	Oid	cmoid;
 
 	Assert(VARATT_IS_COMPRESSED(attr));
 
-	cmoid = GetCompressionOidFromCompressionId(TOAST_COMPRESS_METHOD(attr));
+	/* get the compression method oid */
+	cmoid = toast_get_compression_oid(attr);
 
 	/* get compression method handler routines */
 	cmroutine = GetCompressionRoutine(cmoid);
 
+	/* get the compression header size */
+	header_sz = IsCustomCompression(GetCompressionId(cmoid)) ?
+							TOAST_CUSTOM_COMPRESS_HDRSZ : TOAST_COMPRESS_HDRSZ;
+
 	/*
 	 * If the handler supports the slice decompression then decompress the
 	 * slice otherwise decompress complete data.
 	 */
 	if (cmroutine->cmdecompress_slice)
-		return cmroutine->cmdecompress_slice(attr, slicelength);
+		return cmroutine->cmdecompress_slice(attr, header_sz, slicelength);
 	else
-		return cmroutine->cmdecompress(attr);
+		return cmroutine->cmdecompress(attr, header_sz);
 }
 
 /* ----------
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 7c603dd19a..d69dd907c9 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -38,10 +38,14 @@ static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
  * --------
  */
 void
-toast_set_compressed_datum_info(struct varlena *val, CompressionId cmid, int32 rawsize)
+toast_set_compressed_datum_info(struct varlena *val, CompressionId cmid,
+								Oid cmoid, int32 rawsize)
 {
 	TOAST_COMPRESS_SET_RAWSIZE(val, rawsize);
 	TOAST_COMPRESS_SET_COMPRESSION_METHOD(val, cmid);
+
+	if (IsCustomCompression(cmid))
+		TOAST_COMPRESS_SET_CMID(val, cmoid);
 }
 
 /* ----------
@@ -62,6 +66,7 @@ toast_compress_datum(Datum value, Oid cmoid)
 {
 	struct varlena *tmp = NULL;
 	int32		valsize;
+	CompressionId cmid;
 	CompressionRoutine *cmroutine;
 
 	Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
@@ -73,9 +78,12 @@ toast_compress_datum(Datum value, Oid cmoid)
 
 	/* Get the compression routines for the compression method */
 	cmroutine = GetCompressionRoutine(cmoid);
+	cmid = GetCompressionId(cmoid);
 
 	/* Call the actual compression function */
-	tmp = cmroutine->cmcompress((const struct varlena *) value);
+	tmp = cmroutine->cmcompress((const struct varlena *) value,
+					IsCustomCompression(cmid) ?
+					TOAST_CUSTOM_COMPRESS_HDRSZ : TOAST_COMPRESS_HDRSZ);
 	if (!tmp)
 		return PointerGetDatum(NULL);
 
@@ -94,8 +102,7 @@ toast_compress_datum(Datum value, Oid cmoid)
 	if (VARSIZE(tmp) < valsize - 2)
 	{
 		/* successful compression */
-		toast_set_compressed_datum_info(tmp, GetCompressionId(cmoid),
-										valsize);
+		toast_set_compressed_datum_info(tmp, cmid, cmoid, valsize);
 		return PointerGetDatum(tmp);
 	}
 	else
diff --git a/src/backend/access/compression/compressionapi.c b/src/backend/access/compression/compressionapi.c
index ee57261213..cf91b1c69b 100644
--- a/src/backend/access/compression/compressionapi.c
+++ b/src/backend/access/compression/compressionapi.c
@@ -129,6 +129,6 @@ GetCompressionId(Oid cmoid)
 		case LZ4_COMPRESSION_OID:
 			return LZ4_COMPRESSION_ID;
 		default:
-			elog(ERROR, "Invalid compression method %d", cmoid);
+			return CUSTOM_COMPRESSION_ID;
 	}
 }
diff --git a/src/backend/access/compression/lz4.c b/src/backend/access/compression/lz4.c
index edc8f5ed22..52c167a35d 100644
--- a/src/backend/access/compression/lz4.c
+++ b/src/backend/access/compression/lz4.c
@@ -29,7 +29,7 @@
  * compressed varlena, or NULL if compression fails.
  */
 static struct varlena *
-lz4_cmcompress(const struct varlena *value)
+lz4_cmcompress(const struct varlena *value, int32 header_size)
 {
 	int32		valsize;
 	int32		len;
@@ -39,10 +39,10 @@ lz4_cmcompress(const struct varlena *value)
 	valsize = VARSIZE_ANY_EXHDR(value);
 
 	max_size = LZ4_compressBound(VARSIZE_ANY_EXHDR(value));
-	tmp = (struct varlena *) palloc(max_size + TOAST_COMPRESS_HDRSZ);
+	tmp = (struct varlena *) palloc(max_size + header_size);
 
 	len = LZ4_compress_default(VARDATA_ANY(value),
-				   (char *) tmp + TOAST_COMPRESS_HDRSZ,
+				   (char *) tmp + header_size,
 				   valsize, max_size);
 	if (len <= 0)
 	{
@@ -50,7 +50,7 @@ lz4_cmcompress(const struct varlena *value)
 		elog(ERROR, "lz4: could not compress data");
 	}
 
-	SET_VARSIZE_COMPRESSED(tmp, len + TOAST_COMPRESS_HDRSZ);
+	SET_VARSIZE_COMPRESSED(tmp, len + header_size);
 	return tmp;
 }
 
@@ -60,7 +60,7 @@ lz4_cmcompress(const struct varlena *value)
  * Returns the decompressed varlena.
  */
 static struct varlena *
-lz4_cmdecompress(const struct varlena *value)
+lz4_cmdecompress(const struct varlena *value, int32 header_size)
 {
 	int32		rawsize;
 	struct varlena *result;
@@ -68,9 +68,9 @@ lz4_cmdecompress(const struct varlena *value)
 	result = (struct varlena *) palloc(TOAST_COMPRESS_RAWSIZE(value) + VARHDRSZ);
 	SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(value) + VARHDRSZ);
 
-	rawsize = LZ4_decompress_safe((char *) value + TOAST_COMPRESS_HDRSZ,
+	rawsize = LZ4_decompress_safe((char *) value + header_size,
 								  VARDATA(result),
-								  VARSIZE(value) - TOAST_COMPRESS_HDRSZ,
+								  VARSIZE(value) - header_size,
 								  TOAST_COMPRESS_RAWSIZE(value));
 	if (rawsize < 0)
 		elog(ERROR, "lz4: compressed data is corrupted");
@@ -86,17 +86,18 @@ lz4_cmdecompress(const struct varlena *value)
  * Decompresses part of the data. Returns the decompressed varlena.
  */
 static struct varlena *
-lz4_cmdecompress_slice(const struct varlena *value, int32 slicelength)
+lz4_cmdecompress_slice(const struct varlena *value, int32 header_size,
+					   int32 slicelength)
 {
 	int32		rawsize;
 	struct varlena *result;
 
-	result = (struct varlena *)palloc(slicelength + VARHDRSZ);
+	result = (struct varlena *) palloc(slicelength + VARHDRSZ);
 	SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(value) + VARHDRSZ);
 
-	rawsize = LZ4_decompress_safe_partial((char *) value + TOAST_COMPRESS_HDRSZ,
+	rawsize = LZ4_decompress_safe_partial((char *) value + header_size,
 										  VARDATA(result),
-										  VARSIZE(value) - TOAST_COMPRESS_HDRSZ,
+										  VARSIZE(value) - header_size,
 										  slicelength,
 										  slicelength);
 	if (rawsize < 0)
diff --git a/src/backend/access/compression/pglz.c b/src/backend/access/compression/pglz.c
index 61dfe42004..931394f779 100644
--- a/src/backend/access/compression/pglz.c
+++ b/src/backend/access/compression/pglz.c
@@ -27,7 +27,7 @@
  * compressed varlena, or NULL if compression fails.
  */
 static struct varlena *
-pglz_cmcompress(const struct varlena *value)
+pglz_cmcompress(const struct varlena *value, int32 header_size)
 {
 	int32		valsize,
 				len;
@@ -44,16 +44,16 @@ pglz_cmcompress(const struct varlena *value)
 		return NULL;
 
 	tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
-									TOAST_COMPRESS_HDRSZ);
+									header_size);
 
 	len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
 						valsize,
-						TOAST_COMPRESS_RAWDATA(tmp),
+						(char *) tmp + header_size,
 						NULL);
 
 	if (len >= 0)
 	{
-		SET_VARSIZE_COMPRESSED(tmp, len + TOAST_COMPRESS_HDRSZ);
+		SET_VARSIZE_COMPRESSED(tmp, len + header_size);
 		return tmp;
 	}
 
@@ -68,7 +68,7 @@ pglz_cmcompress(const struct varlena *value)
  * Returns the decompressed varlena.
  */
 static struct varlena *
-pglz_cmdecompress(const struct varlena *value)
+pglz_cmdecompress(const struct varlena *value, int32 header_size)
 {
 	struct varlena *result;
 	int32		rawsize;
@@ -76,8 +76,8 @@ pglz_cmdecompress(const struct varlena *value)
 	result = (struct varlena *) palloc(TOAST_COMPRESS_RAWSIZE(value) + VARHDRSZ);
 	SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(value) + VARHDRSZ);
 
-	rawsize = pglz_decompress(TOAST_COMPRESS_RAWDATA(value),
-							  TOAST_COMPRESS_SIZE(value),
+	rawsize = pglz_decompress((char *) value + header_size,
+							  VARSIZE(value) - header_size,
 							  VARDATA(result),
 							  TOAST_COMPRESS_RAWSIZE(value), true);
 
@@ -95,7 +95,7 @@ pglz_cmdecompress(const struct varlena *value)
  * Decompresses part of the data. Returns the decompressed varlena.
  */
 static struct varlena *
-pglz_cmdecompress_slice(const struct varlena *value,
+pglz_cmdecompress_slice(const struct varlena *value, int32 header_size,
 						int32 slicelength)
 {
 	struct varlena *result;
@@ -103,8 +103,8 @@ pglz_cmdecompress_slice(const struct varlena *value,
 
 	result = (struct varlena *) palloc(slicelength + VARHDRSZ);
 
-	rawsize = pglz_decompress(TOAST_COMPRESS_RAWDATA(value),
-							  VARSIZE(value) - TOAST_COMPRESS_HDRSZ,
+	rawsize = pglz_decompress((char *) value + header_size,
+							  VARSIZE(value) - header_size,
 							  VARDATA(result),
 							  slicelength, false);
 
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index c626161408..bf47b230b1 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3410,6 +3410,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
 					case OBJECT_AMPROC:
 					case OBJECT_ATTRIBUTE:
 					case OBJECT_CAST:
+					case OBJECT_COMPRESSION_METHOD:
 					case OBJECT_DEFAULT:
 					case OBJECT_DEFACL:
 					case OBJECT_DOMCONSTRAINT:
@@ -3549,6 +3550,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
 					case OBJECT_AMPROC:
 					case OBJECT_ATTRIBUTE:
 					case OBJECT_CAST:
+					case OBJECT_COMPRESSION_METHOD:
 					case OBJECT_DEFAULT:
 					case OBJECT_DEFACL:
 					case OBJECT_DOMCONSTRAINT:
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 076ef56ba5..c928610c14 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1566,6 +1566,7 @@ doDeletion(const ObjectAddress *object, int flags)
 		case OCLASS_AM:
 		case OCLASS_AMOP:
 		case OCLASS_AMPROC:
+		case OCLASS_COMPRESSION:
 		case OCLASS_SCHEMA:
 		case OCLASS_TSPARSER:
 		case OCLASS_TSDICT:
@@ -1587,7 +1588,6 @@ doDeletion(const ObjectAddress *object, int flags)
 		case OCLASS_DATABASE:
 		case OCLASS_TBLSPACE:
 		case OCLASS_SUBSCRIPTION:
-		case OCLASS_COMPRESSION:
 			elog(ERROR, "global objects cannot be deleted by doDeletion");
 			break;
 
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8f95da758f..f80305cf44 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -189,6 +189,20 @@ static const ObjectPropertyType ObjectProperty[] =
 		OBJECT_COLLATION,
 		true
 	},
+	{
+		"compression method",
+		CompressionRelationId,
+		CompressionIndexId,
+		CMOID,
+		CMNAME,
+		Anum_pg_compression_oid,
+		Anum_pg_compression_cmname,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		-1,
+		true
+	},
 	{
 		"constraint",
 		ConstraintRelationId,
@@ -1009,6 +1023,7 @@ get_object_address(ObjectType objtype, Node *object,
 			case OBJECT_FOREIGN_SERVER:
 			case OBJECT_EVENT_TRIGGER:
 			case OBJECT_ACCESS_METHOD:
+			case OBJECT_COMPRESSION_METHOD:
 			case OBJECT_PUBLICATION:
 			case OBJECT_SUBSCRIPTION:
 				address = get_object_address_unqualified(objtype,
@@ -1260,6 +1275,11 @@ get_object_address_unqualified(ObjectType objtype,
 			address.objectId = get_am_oid(name, missing_ok);
 			address.objectSubId = 0;
 			break;
+		case OBJECT_COMPRESSION_METHOD:
+			address.classId = CompressionRelationId;
+			address.objectId = get_cm_oid(name, missing_ok);
+			address.objectSubId = 0;
+			break;
 		case OBJECT_DATABASE:
 			address.classId = DatabaseRelationId;
 			address.objectId = get_database_oid(name, missing_ok);
@@ -2271,6 +2291,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 			objnode = (Node *) name;
 			break;
 		case OBJECT_ACCESS_METHOD:
+		case OBJECT_COMPRESSION_METHOD:
 		case OBJECT_DATABASE:
 		case OBJECT_EVENT_TRIGGER:
 		case OBJECT_EXTENSION:
@@ -2564,6 +2585,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 		case OBJECT_TSPARSER:
 		case OBJECT_TSTEMPLATE:
 		case OBJECT_ACCESS_METHOD:
+		case OBJECT_COMPRESSION_METHOD:
 			/* We treat these object types as being owned by superusers */
 			if (!superuser_arg(roleid))
 				ereport(ERROR,
diff --git a/src/backend/commands/compressioncmds.c b/src/backend/commands/compressioncmds.c
index a1be6ef7be..ce6f7f83f5 100644
--- a/src/backend/commands/compressioncmds.c
+++ b/src/backend/commands/compressioncmds.c
@@ -20,11 +20,115 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_attribute.h"
+#include "catalog/pg_compression.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_proc.h"
 #include "commands/defrem.h"
+#include "miscadmin.h"
 #include "nodes/parsenodes.h"
+#include "parser/parse_func.h"
 #include "utils/fmgroids.h"
+#include "utils/fmgrprotos.h"
+#include "utils/syscache.h"
+
+/*
+ * CreateCompressionMethod
+ *		Registers a new compression method.
+ */
+ObjectAddress
+CreateCompressionMethod(CreateCmStmt *stmt)
+{
+	Relation	rel;
+	ObjectAddress myself;
+	ObjectAddress referenced;
+	Oid			cmoid;
+	Oid			cmhandler;
+	bool		nulls[Natts_pg_compression];
+	Datum		values[Natts_pg_compression];
+	HeapTuple	tup;
+	Oid			funcargtypes[1] = {INTERNALOID};
+
+	rel = table_open(CompressionRelationId, RowExclusiveLock);
+
+	/* Must be super user */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied to create compression method \"%s\"",
+						stmt->cmname),
+				 errhint("Must be superuser to create an access method.")));
+
+	/* Check if name is used */
+	cmoid = GetSysCacheOid1(CMNAME, Anum_pg_compression_oid,
+							CStringGetDatum(stmt->cmname));
+	if (OidIsValid(cmoid))
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("compression method \"%s\" already exists",
+						stmt->cmname)));
+	}
+
+	if (stmt->handler_name == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("handler function is not specified")));
+
+	/* Get the handler function oid */
+	cmhandler = LookupFuncName(stmt->handler_name, 1, funcargtypes, false);
+
+	/*
+	 * Insert tuple into pg_compression.
+	 */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+
+	cmoid = GetNewOidWithIndex(rel, CompressionIndexId, Anum_pg_compression_oid);
+	values[Anum_pg_compression_oid - 1] = ObjectIdGetDatum(cmoid);
+	values[Anum_pg_compression_cmname - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(stmt->cmname));
+	values[Anum_pg_compression_cmhandler - 1] = ObjectIdGetDatum(cmhandler);
+
+	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+	CatalogTupleInsert(rel, tup);
+	heap_freetuple(tup);
+
+	myself.classId = CompressionRelationId;
+	myself.objectId = cmoid;
+	myself.objectSubId = 0;
+
+	/* Record dependency on handler function */
+	referenced.classId = ProcedureRelationId;
+	referenced.objectId = cmhandler;
+	referenced.objectSubId = 0;
+
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	InvokeObjectPostCreateHook(CompressionRelationId, cmoid, 0);
+
+	table_close(rel, RowExclusiveLock);
+
+	return myself;
+}
+
+Oid
+get_cm_oid(const char *cmname, bool missing_ok)
+{
+	Oid			cmoid;
+
+	cmoid = GetCompressionOid(cmname);
+	if (!OidIsValid(cmoid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("compression type \"%s\" not recognized", cmname)));
+
+	return cmoid;
+}
 
 /*
  * get list of all supported compression methods for the given attribute.
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 172242ae37..b2a07bc977 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -953,6 +953,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_CAST:
 		case OBJECT_COLUMN:
 		case OBJECT_COLLATION:
+		case OBJECT_COMPRESSION_METHOD:
 		case OBJECT_CONVERSION:
 		case OBJECT_DEFACL:
 		case OBJECT_DEFAULT:
@@ -2106,6 +2107,7 @@ stringify_grant_objtype(ObjectType objtype)
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLLATION:
+		case OBJECT_COMPRESSION_METHOD:
 		case OBJECT_CONVERSION:
 		case OBJECT_DEFAULT:
 		case OBJECT_DEFACL:
@@ -2188,6 +2190,7 @@ stringify_adefprivs_objtype(ObjectType objtype)
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLLATION:
+		case OBJECT_COMPRESSION_METHOD:
 		case OBJECT_CONVERSION:
 		case OBJECT_DEFAULT:
 		case OBJECT_DEFACL:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index ee036e9087..083ac78e11 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -67,6 +67,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLLATION:
+		case OBJECT_COMPRESSION_METHOD:
 		case OBJECT_CONVERSION:
 		case OBJECT_DEFAULT:
 		case OBJECT_DEFACL:
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 5257aeb66b..6653bd4d9a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1970,8 +1970,7 @@ CompareCompressionMethodAndDecompress(TupleTableSlot *slot,
 			 * Get the compression method stored in the toast header and
 			 * compare with the compression method of the target.
 			 */
-			cmoid = GetCompressionOidFromCompressionId(
-									TOAST_COMPRESS_METHOD(new_value));
+			cmoid = toast_get_compression_oid(new_value);
 			if (!IsCompressionSupported(&targetTupDesc->attrs[i], cmoid))
 			{
 				new_value = detoast_attr(new_value);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5413bd92a4..707c6ea90b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4312,6 +4312,17 @@ _copyCreateAmStmt(const CreateAmStmt *from)
 	return newnode;
 }
 
+static CreateCmStmt *
+_copyCreateCmStmt(const CreateCmStmt *from)
+{
+	CreateCmStmt *newnode = makeNode(CreateCmStmt);
+
+	COPY_STRING_FIELD(cmname);
+	COPY_NODE_FIELD(handler_name);
+
+	return newnode;
+}
+
 static CreateTrigStmt *
 _copyCreateTrigStmt(const CreateTrigStmt *from)
 {
@@ -5474,6 +5485,9 @@ copyObjectImpl(const void *from)
 		case T_CreateAmStmt:
 			retval = _copyCreateAmStmt(from);
 			break;
+		case T_CreateCmStmt:
+			retval = _copyCreateCmStmt(from);
+			break;
 		case T_CreateTrigStmt:
 			retval = _copyCreateTrigStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 09882c9375..0c17cec725 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2008,6 +2008,15 @@ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
 	return true;
 }
 
+static bool
+_equalCreateCmStmt(const CreateCmStmt *a, const CreateCmStmt *b)
+{
+	COMPARE_STRING_FIELD(cmname);
+	COMPARE_NODE_FIELD(handler_name);
+
+	return true;
+}
+
 static bool
 _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 {
@@ -3527,6 +3536,9 @@ equal(const void *a, const void *b)
 		case T_CreateAmStmt:
 			retval = _equalCreateAmStmt(a, b);
 			break;
+		case T_CreateCmStmt:
+			retval = _equalCreateCmStmt(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 deb82ea660..d21cb7c94c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -289,7 +289,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		DeallocateStmt PrepareStmt ExecuteStmt
 		DropOwnedStmt ReassignOwnedStmt
 		AlterTSConfigurationStmt AlterTSDictionaryStmt
-		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
+		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt CreateCmStmt
 		CreatePublicationStmt AlterPublicationStmt
 		CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt
 
@@ -881,6 +881,7 @@ stmt :
 			| CreateAsStmt
 			| CreateAssertionStmt
 			| CreateCastStmt
+			| CreateCmStmt
 			| CreateConversionStmt
 			| CreateDomainStmt
 			| CreateExtensionStmt
@@ -5263,6 +5264,22 @@ am_type:
 		|	TABLE			{ $$ = AMTYPE_TABLE; }
 		;
 
+/*****************************************************************************
+ *
+ *		QUERY:
+ *             CREATE COMPRESSION METHOD name HANDLER handler_name
+ *
+ *****************************************************************************/
+
+CreateCmStmt: CREATE COMPRESSION METHOD name HANDLER handler_name
+				{
+					CreateCmStmt *n = makeNode(CreateCmStmt);
+					n->cmname = $4;
+					n->handler_name = $6;
+					$$ = (Node *) n;
+				}
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
@@ -6265,6 +6282,7 @@ object_type_name:
 
 drop_type_name:
 			ACCESS METHOD							{ $$ = OBJECT_ACCESS_METHOD; }
+			| COMPRESSION METHOD					{ $$ = OBJECT_COMPRESSION_METHOD; }
 			| EVENT TRIGGER							{ $$ = OBJECT_EVENT_TRIGGER; }
 			| EXTENSION								{ $$ = OBJECT_EXTENSION; }
 			| FOREIGN DATA_P WRAPPER				{ $$ = OBJECT_FDW; }
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f398027fa6..dfd79d1007 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -168,6 +168,7 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
 		case T_CompositeTypeStmt:
 		case T_CreateAmStmt:
 		case T_CreateCastStmt:
+		case T_CreateCmStmt:
 		case T_CreateConversionStmt:
 		case T_CreateDomainStmt:
 		case T_CreateEnumStmt:
@@ -1805,6 +1806,10 @@ ProcessUtilitySlow(ParseState *pstate,
 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
 				break;
 
+			case T_CreateCmStmt:
+				address = CreateCompressionMethod((CreateCmStmt *) parsetree);
+				break;
+
 			case T_CreatePublicationStmt:
 				address = CreatePublication((CreatePublicationStmt *) parsetree);
 				break;
@@ -2562,6 +2567,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_ACCESS_METHOD:
 					tag = CMDTAG_DROP_ACCESS_METHOD;
 					break;
+				case OBJECT_COMPRESSION_METHOD:
+					tag = CMDTAG_DROP_COMPRESSION_METHOD;
+					break;
 				case OBJECT_PUBLICATION:
 					tag = CMDTAG_DROP_PUBLICATION;
 					break;
@@ -2969,6 +2977,10 @@ CreateCommandTag(Node *parsetree)
 			tag = CMDTAG_CREATE_ACCESS_METHOD;
 			break;
 
+		case T_CreateCmStmt:
+			tag = CMDTAG_CREATE_COMPRESSION_METHOD;
+			break;
+
 		case T_CreatePublicationStmt:
 			tag = CMDTAG_CREATE_PUBLICATION;
 			break;
@@ -3573,6 +3585,10 @@ GetCommandLogLevel(Node *parsetree)
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_CreateCmStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_CreatePublicationStmt:
 			lev = LOGSTMT_DDL;
 			break;
diff --git a/src/include/access/compressionapi.h b/src/include/access/compressionapi.h
index 6660285e84..4d4326ff57 100644
--- a/src/include/access/compressionapi.h
+++ b/src/include/access/compressionapi.h
@@ -23,23 +23,31 @@
  * this in the first 2 bits of the raw length.  These built-in compression
  * method-id are directly mapped to the built-in compression method oid.  And,
  * using that oid we can get the compression handler routine by fetching the
- * pg_compression catalog row.
+ * pg_compression catalog row.  If it is custome compression id then the
+ * compressed data will store special custom compression header wherein it will
+ * directly store the oid of the custom compression method.
  */
 typedef enum CompressionId
 {
-	PGLZ_COMPRESSION_ID,
-	LZ4_COMPRESSION_ID
+	PGLZ_COMPRESSION_ID = 0,
+	LZ4_COMPRESSION_ID = 1,
+	/* one free slot for the future built-in method */
+	CUSTOM_COMPRESSION_ID = 3
 } CompressionId;
 
-/* Use default compression method if it is not specified. */
-#define DefaultCompressionOid	PGLZ_COMPRESSION_OID
+/* Use default compression method if it is not specified */
+#define DefaultCompressionOid		PGLZ_COMPRESSION_OID
+#define IsCustomCompression(cmid)	((cmid) == CUSTOM_COMPRESSION_ID)
 
 typedef struct CompressionRoutine CompressionRoutine;
 
 /* compresion handler routines */
-typedef struct varlena *(*cmcompress_function) (const struct varlena *value);
-typedef struct varlena *(*cmdecompress_slice_function)
-			(const struct varlena *value, int32 slicelength);
+typedef struct varlena *(*cmcompress_function)(const struct varlena *value,
+											   int32 toast_header_size);
+typedef struct varlena *(*cmdecompress_slice_function)(
+												const struct varlena *value,
+												int32 slicelength,
+												int32 toast_header_size);
 
 /*
  * API struct for a compression.
diff --git a/src/include/access/detoast.h b/src/include/access/detoast.h
index 86bad7e78c..e6b3ec90b5 100644
--- a/src/include/access/detoast.h
+++ b/src/include/access/detoast.h
@@ -89,4 +89,12 @@ extern Size toast_raw_datum_size(Datum value);
  */
 extern Size toast_datum_size(Datum value);
 
+/* ----------
+ * toast_get_compression_oid -
+ *
+ *	Return the compression method oid from the compressed value
+ * ----------
+ */
+extern Oid toast_get_compression_oid(struct varlena *attr);
+
 #endif							/* DETOAST_H */
diff --git a/src/include/access/toast_internals.h b/src/include/access/toast_internals.h
index 8ef477cfe0..48ca172eae 100644
--- a/src/include/access/toast_internals.h
+++ b/src/include/access/toast_internals.h
@@ -27,6 +27,17 @@ typedef struct toast_compress_header
 								 * rawsize */
 } toast_compress_header;
 
+/*
+ * If the compression method were used, then data also contains
+ * Oid of compression options
+ */
+typedef struct toast_compress_header_custom
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	uint32		info;			/*  2 bits for compression method + rawsize */
+	Oid			cmoid;			/* Oid from pg_compression */
+} toast_compress_header_custom;
+
 #define RAWSIZEMASK (0x3FFFFFFFU)
 
 /*
@@ -38,6 +49,7 @@ typedef struct toast_compress_header
  * two highest bits.
  */
 #define TOAST_COMPRESS_HDRSZ		((int32) sizeof(toast_compress_header))
+#define TOAST_CUSTOM_COMPRESS_HDRSZ	((int32) sizeof(toast_compress_header_custom))
 #define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->info & RAWSIZEMASK)
 #define TOAST_COMPRESS_METHOD(ptr)  (((toast_compress_header *) (ptr))->info >> 30)
 #define TOAST_COMPRESS_SIZE(ptr)	((int32) VARSIZE_ANY(ptr) - TOAST_COMPRESS_HDRSZ)
@@ -52,6 +64,9 @@ do { \
 #define TOAST_COMPRESS_SET_COMPRESSION_METHOD(ptr, cm_method) \
 	((toast_compress_header *) (ptr))->info |= ((cm_method) << 30);
 
+#define TOAST_COMPRESS_SET_CMID(ptr, oid) \
+	(((toast_compress_header_custom *) (ptr))->cmoid = (oid))
+
 extern Datum toast_compress_datum(Datum value, Oid cmoid);
 extern Oid	toast_get_valid_index(Oid toastoid, LOCKMODE lock);
 
@@ -72,7 +87,9 @@ extern void init_toast_snapshot(Snapshot toast_snapshot);
  *
  * Save metadata in compressed datum
  */
-extern void toast_set_compressed_datum_info(struct varlena *val, CompressionId cmid,
+extern void toast_set_compressed_datum_info(struct varlena *val,
+											CompressionId cmid,
+											Oid cmoid,
 											int32 rawsize);
 
 #endif							/* TOAST_INTERNALS_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 936b072c76..8952b2b70d 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -146,6 +146,8 @@ extern Oid	get_am_oid(const char *amname, bool missing_ok);
 extern char *get_am_name(Oid amOid);
 
 /* commands/compressioncmds.c */
+extern ObjectAddress CreateCompressionMethod(CreateCmStmt *stmt);
+extern Oid get_cm_oid(const char *cmname, bool missing_ok);
 extern Oid GetAttributeCompression(Form_pg_attribute att,
 								   ColumnCompression *compression,
 								   bool *need_rewrite);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 65ef987d67..ae297c9116 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -415,6 +415,7 @@ typedef enum NodeTag
 	T_AlterPolicyStmt,
 	T_CreateTransformStmt,
 	T_CreateAmStmt,
+	T_CreateCmStmt,
 	T_CreatePublicationStmt,
 	T_AlterPublicationStmt,
 	T_CreateSubscriptionStmt,
@@ -493,7 +494,7 @@ typedef enum NodeTag
 	T_TimeLineHistoryCmd,
 	T_SQLCmd,
 
-	T_CompressionRoutine,		/* in access/compressionapi.h */
+	T_CompressionRoutine, /* in access/compressionapi.h */
 	/*
 	 * TAGS FOR RANDOM OTHER STUFF
 	 *
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e0a51f0145..89478ddea3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1722,6 +1722,7 @@ typedef enum ObjectType
 	OBJECT_CAST,
 	OBJECT_COLUMN,
 	OBJECT_COLLATION,
+	OBJECT_COMPRESSION_METHOD,
 	OBJECT_CONVERSION,
 	OBJECT_DATABASE,
 	OBJECT_DEFAULT,
@@ -2435,6 +2436,17 @@ typedef struct CreateAmStmt
 	char		amtype;			/* type of access method */
 } CreateAmStmt;
 
+/*----------------------
+ *		Create COMPRESSION METHOD Statement
+ *----------------------
+ */
+typedef struct CreateCmStmt
+{
+	NodeTag		type;
+	char	   *cmname;			/* compression method name */
+	List	   *handler_name;	/* handler function name */
+} CreateCmStmt;
+
 /* ----------------------
  *		Create TRIGGER Statement
  * ----------------------
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 4e7cad3daf..a8893759cf 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -149,6 +149,14 @@ typedef union
 								 * flags */
 		char		va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
 	}			va_compressed;
+	struct						/* Compressed-in-line format */
+	{
+		uint32		va_header;
+		uint32		va_info;	/* Original data size (excludes header) and
+								 * flags */
+		Oid			va_cmid;	/* Oid of compression method */
+		char		va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
+	}			va_custom_compressed;
 } varattrib_4b;
 
 typedef struct
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index be94852bbd..a9bc1c4bae 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -83,6 +83,7 @@ PG_CMDTAG(CMDTAG_COMMIT_PREPARED, "COMMIT PREPARED", false, false, false)
 PG_CMDTAG(CMDTAG_COPY, "COPY", false, false, true)
 PG_CMDTAG(CMDTAG_COPY_FROM, "COPY FROM", false, false, false)
 PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_COMPRESSION_METHOD, "CREATE COMPRESSION METHOD", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false)
 PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false)
@@ -138,6 +139,7 @@ PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_COMPRESSION_METHOD, "DROP COMPRESSIOn METHOD", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false)
 PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false)
diff --git a/src/test/regress/expected/create_cm.out b/src/test/regress/expected/create_cm.out
index 1b7b5bd6e5..897a61689f 100644
--- a/src/test/regress/expected/create_cm.out
+++ b/src/test/regress/expected/create_cm.out
@@ -103,4 +103,22 @@ INSERT INTO cmmove2 VALUES (repeat('1234567890',1004));
 --------+------+-----------+----------+---------+----------+-------------+--------------+-------------
  f1     | text |           |          |         | extended | pglz        |              | 
 
+-- create compression method
+CREATE COMPRESSION METHOD pglz2 HANDLER pglzhandler;
+ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION pglz2;
+INSERT INTO cmmove2 VALUES (repeat('1234567890',1004));
+SELECT length(f1) FROM cmmove2;
+ length 
+--------
+  10000
+  10040
+  10040
+(3 rows)
+
+\d+ cmmove2
+                                        Table "public.cmmove2"
+ Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1     | text |           |          |         | extended | pglz2       |              | 
+
 DROP TABLE cmmove1, cmmove2, cmmove3, lz4test;
diff --git a/src/test/regress/sql/create_cm.sql b/src/test/regress/sql/create_cm.sql
index 3549fb35f0..a0a0aa13f0 100644
--- a/src/test/regress/sql/create_cm.sql
+++ b/src/test/regress/sql/create_cm.sql
@@ -51,4 +51,11 @@ ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION pglz PRESERVE (lz4);
 INSERT INTO cmmove2 VALUES (repeat('1234567890',1004));
 \d+ cmmove2
 
+-- create compression method
+CREATE COMPRESSION METHOD pglz2 HANDLER pglzhandler;
+ALTER TABLE cmmove2 ALTER COLUMN f1 SET COMPRESSION pglz2;
+INSERT INTO cmmove2 VALUES (repeat('1234567890',1004));
+SELECT length(f1) FROM cmmove2;
+\d+ cmmove2
+
 DROP TABLE cmmove1, cmmove2, cmmove3, lz4test;
-- 
2.23.0