From 9affe916f84e8ea3bd46bc793773e1281980502f Mon Sep 17 00:00:00 2001
From: vignesh <vignesh21@gmail.com>
Date: Wed, 19 May 2021 12:18:00 +0530
Subject: [PATCH v1 2/2] Implement type regsubscription

It will help in casting subscription information and it's also just generally
useful, like the other reg* types.
---
 doc/src/sgml/datatype.sgml            |  11 +++
 doc/src/sgml/func.sgml                |  18 ++++
 doc/src/sgml/ref/pgupgrade.sgml       |   1 +
 src/backend/utils/adt/regproc.c       | 125 ++++++++++++++++++++++++++
 src/bin/pg_upgrade/check.c            |   3 +-
 src/include/catalog/pg_cast.dat       |  14 +++
 src/include/catalog/pg_proc.dat       |  15 ++++
 src/include/catalog/pg_type.dat       |   4 +
 src/test/regress/expected/regproc.out |  53 +++++++++++
 src/test/regress/sql/regproc.sql      |  13 +++
 10 files changed, 256 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index dd578f3535..67fbce1a7f 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4607,6 +4607,10 @@ INSERT INTO mytable VALUES(-1);  -- fails
     <primary>regpublication</primary>
    </indexterm>
 
+   <indexterm zone="datatype-oid">
+    <primary>regsubscription</primary>
+   </indexterm>
+
    <indexterm zone="datatype-oid">
     <primary>regrole</primary>
    </indexterm>
@@ -4774,6 +4778,13 @@ SELECT * FROM pg_attribute
         <entry><literal>testpub</literal></entry>
        </row>
 
+       <row>
+        <entry><type>regsubscription</type></entry>
+        <entry><structname>pg_subscription</structname></entry>
+        <entry>subscription name</entry>
+        <entry><literal>testsub</literal></entry>
+       </row>
+
        <row>
         <entry><type>regrole</type></entry>
         <entry><structname>pg_authid</structname></entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 50da778b06..e487a02064 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -23614,6 +23614,24 @@ SELECT collation for ('foo' COLLATE "de_DE");
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>to_regsubscription</primary>
+        </indexterm>
+        <function>to_regsubscription</function> ( <type>text</type> )
+        <returnvalue>regsubscritpion</returnvalue>
+       </para>
+       <para>
+        Translates a textual subscription name to its OID.  A similar result is
+        obtained by casting the string to type <type>regsubscription</type> (see
+        <xref linkend="datatype-oid"/>); however, this function will return
+        <literal>NULL</literal> rather than throwing an error if the name is
+        not found.  Also unlike the cast, this does not accept
+        a numeric OID as input.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index bb2d908da6..5616bd28b8 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -786,6 +786,7 @@ psql --username=postgres --file=script.sql postgres
     <member><type>regproc</type></member>
     <member><type>regprocedure</type></member>
     <member><type>regpublication</type></member>
+    <member><type>regsubscription</type></member>
    </simplelist>
    (<type>regclass</type>, <type>regrole</type>, and <type>regtype</type> can be upgraded.)
   </para>
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index dfa826f1c2..e8e3c6ce9a 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -28,6 +28,7 @@
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_publication.h"
+#include "catalog/pg_subscription.h"
 #include "catalog/pg_ts_config.h"
 #include "catalog/pg_ts_dict.h"
 #include "catalog/pg_type.h"
@@ -1357,6 +1358,130 @@ regpublicationsend(PG_FUNCTION_ARGS)
 	return oidsend(fcinfo);
 }
 
+/*
+ * regsubscriptionin		- converts "subscriptionname" to subscription OID
+ *
+ * We also accept a numeric OID, for symmetry with the output routine.
+ *
+ * '-' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_subscription entry.
+ */
+Datum
+regsubscriptionin(PG_FUNCTION_ARGS)
+{
+	char	   *subscription_name_or_oid = PG_GETARG_CSTRING(0);
+	Oid			result = InvalidOid;
+	List	   *names;
+
+	/* '-' ? */
+	if (strcmp(subscription_name_or_oid, "-") == 0)
+		PG_RETURN_OID(InvalidOid);
+
+	/* Numeric OID? */
+	if (subscription_name_or_oid[0] >= '0' &&
+		subscription_name_or_oid[0] <= '9' &&
+		strspn(subscription_name_or_oid, "0123456789") == strlen(subscription_name_or_oid))
+	{
+		result = DatumGetObjectId(DirectFunctionCall1(oidin,
+													  CStringGetDatum(subscription_name_or_oid)));
+		PG_RETURN_OID(result);
+	}
+
+	/* The rest of this wouldn't work in bootstrap mode */
+	if (IsBootstrapProcessingMode())
+		elog(ERROR, "regsubscription values must be OIDs in bootstrap mode");
+
+	/*
+	 * Normal case: parse the name into components and see if it matches any
+	 * pg_subscription entries in the current search path.
+	 */
+	names = stringToQualifiedNameList(subscription_name_or_oid);
+	if (list_length(names) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_NAME),
+				 errmsg("invalid name syntax")));
+
+	result = get_subscription_oid(strVal(linitial(names)), false);
+
+	PG_RETURN_OID(result);
+}
+
+/*
+ * to_regsubscription		- converts "subscriptionname" to subscription OID
+ *
+ * If the name is not found, we return NULL.
+ */
+Datum
+to_regsubscription(PG_FUNCTION_ARGS)
+{
+	char	   *subscription_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	Oid			result;
+	List	   *names;
+
+	names = stringToQualifiedNameList(subscription_name);
+	if (list_length(names) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_NAME),
+				 errmsg("invalid name syntax")));
+
+	result = get_subscription_oid(strVal(linitial(names)), true);
+
+	if (OidIsValid(result))
+		PG_RETURN_OID(result);
+	else
+		PG_RETURN_NULL();
+}
+
+/*
+ * regsubscriptionout		- converts subscription OID to "subscription_name"
+ */
+Datum
+regsubscriptionout(PG_FUNCTION_ARGS)
+{
+	Oid			subscriptionid = PG_GETARG_OID(0);
+	char	   *result;
+
+	if (subscriptionid == InvalidOid)
+	{
+		result = pstrdup("-");
+		PG_RETURN_CSTRING(result);
+	}
+
+	result = get_subscription_name(subscriptionid, true);
+	if (result)
+	{
+		/* pstrdup is not really necessary, but it avoids a compiler warning */
+		result = pstrdup(quote_identifier(result));
+	}
+	else
+	{
+		/* If OID doesn't match any subscription, return it numerically */
+		result = (char *) palloc(NAMEDATALEN);
+		snprintf(result, NAMEDATALEN, "%u", subscriptionid);
+	}
+
+	PG_RETURN_CSTRING(result);
+}
+
+/*
+ *		regsubscriptionrecv			- converts external binary format to regsubscription
+ */
+Datum
+regsubscriptionrecv(PG_FUNCTION_ARGS)
+{
+	/* Exactly the same as oidrecv, so share code */
+	return oidrecv(fcinfo);
+}
+
+/*
+ *		regsubscriptionsend			- converts regsubscription to binary format
+ */
+Datum
+regsubscriptionsend(PG_FUNCTION_ARGS)
+{
+	/* Exactly the same as oidsend, so share code */
+	return oidsend(fcinfo);
+}
 
 /*
  * regtypein		- converts "typename" to type OID
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 8037874af5..ad2a6fd275 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -1142,7 +1142,8 @@ check_for_reg_data_type_usage(ClusterInfo *cluster)
 									   "           'regoperator', "
 									   "           'regproc', "
 									   "           'regprocedure', "
-									   "		   'regpublication' "
+									   "		   'regpublication', "
+									   "		   'regsubscription' "
 	/* pg_authid.oid is preserved, so 'regrole' is OK */
 	/* pg_type.oid is (mostly) preserved, so 'regtype' is OK */
 									   "         )",
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 63007d6527..a6871f6b33 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -221,6 +221,20 @@
   castcontext => 'a', castmethod => 'f' },
 { castsource => 'regpublication', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
+{ castsource => 'oid', casttarget => 'regsubscription', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'regsubscription', casttarget => 'oid', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'int8', casttarget => 'regsubscription', castfunc => 'oid',
+  castcontext => 'i', castmethod => 'f' },
+{ castsource => 'int2', casttarget => 'regsubscription', castfunc => 'int4(int2)',
+  castcontext => 'i', castmethod => 'f' },
+{ castsource => 'int4', casttarget => 'regsubscription', castfunc => '0',
+  castcontext => 'i', castmethod => 'b' },
+{ castsource => 'regsubscription', casttarget => 'int8', castfunc => 'int8(oid)',
+  castcontext => 'a', castmethod => 'f' },
+{ castsource => 'regsubscription', casttarget => 'int4', castfunc => '0',
+  castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regtype', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regtype', casttarget => 'oid', castfunc => '0',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 11c6967348..9d68f2dd56 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6891,6 +6891,15 @@
 { oid => '4546', descr => 'convert classname to regpublication',
   proname => 'to_regpublication', provolatile => 's', prorettype => 'regpublication',
   proargtypes => 'text', prosrc => 'to_regpublication' },
+{ oid => '4551', descr => 'I/O',
+  proname => 'regsubscriptionin', provolatile => 's', prorettype => 'regsubscription',
+  proargtypes => 'cstring', prosrc => 'regsubscriptionin' },
+{ oid => '4552', descr => 'I/O',
+  proname => 'regsubscriptionout', provolatile => 's', prorettype => 'cstring',
+  proargtypes => 'regsubscription', prosrc => 'regsubscriptionout' },
+{ oid => '4553', descr => 'convert classname to regsubscription',
+  proname => 'to_regsubscription', provolatile => 's', prorettype => 'regsubscription',
+  proargtypes => 'text', prosrc => 'to_regsubscription' },
 { oid => '2220', descr => 'I/O',
   proname => 'regtypein', provolatile => 's', prorettype => 'regtype',
   proargtypes => 'cstring', prosrc => 'regtypein' },
@@ -7730,6 +7739,12 @@
 { oid => '4548', descr => 'I/O',
   proname => 'regpublicationsend', prorettype => 'bytea',
   proargtypes => 'regpublication', prosrc => 'regpublicationsend' },
+{ oid => '4554', descr => 'I/O',
+  proname => 'regsubscriptionrecv', prorettype => 'regsubscription',
+  proargtypes => 'internal', prosrc => 'regsubscriptionrecv' },
+{ oid => '4555', descr => 'I/O',
+  proname => 'regsubscriptionsend', prorettype => 'bytea',
+  proargtypes => 'regsubscription', prosrc => 'regsubscriptionsend' },
 { oid => '2454', descr => 'I/O',
   proname => 'regtyperecv', prorettype => 'regtype', proargtypes => 'internal',
   prosrc => 'regtyperecv' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index cf2b7d1890..61a965510f 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -389,6 +389,10 @@
   typname => 'regpublication', typlen => '4', typbyval => 't', typcategory => 'N',
   typinput => 'regpublicationin', typoutput => 'regpublicationout',
   typreceive => 'regpublicationrecv', typsend => 'regpublicationsend', typalign => 'i' },
+{ oid => '4556', array_type_oid => '4557', descr => 'registered subscription',
+  typname => 'regsubscription', typlen => '4', typbyval => 't', typcategory => 'N',
+  typinput => 'regsubscriptionin', typoutput => 'regsubscriptionout',
+  typreceive => 'regsubscriptionrecv', typsend => 'regsubscriptionsend', typalign => 'i' },
 { oid => '2206', array_type_oid => '2211', descr => 'registered type',
   typname => 'regtype', typlen => '4', typbyval => 't', typcategory => 'N',
   typinput => 'regtypein', typoutput => 'regtypeout',
diff --git a/src/test/regress/expected/regproc.out b/src/test/regress/expected/regproc.out
index 2801751718..49fda6444a 100644
--- a/src/test/regress/expected/regproc.out
+++ b/src/test/regress/expected/regproc.out
@@ -6,6 +6,7 @@ CREATE ROLE regress_regrole_test;
 -- suppress warning that depends on wal_level
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION regress_testpub;
+CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false);
 RESET client_min_messages;
 -- without schemaname
 SELECT regoper('||/');
@@ -208,6 +209,18 @@ SELECT regpublication('"regress_testpub"');
  regress_testpub
 (1 row)
 
+SELECT regsubscription('regress_testsub');
+ regsubscription 
+-----------------
+ regress_testsub
+(1 row)
+
+SELECT regsubscription('"regress_testsub"');
+ regsubscription 
+-----------------
+ regress_testsub
+(1 row)
+
 SELECT to_regrole('regress_regrole_test');
       to_regrole      
 ----------------------
@@ -244,9 +257,23 @@ SELECT to_regpublication('"regress_testpub"');
  regress_testpub
 (1 row)
 
+SELECT to_regsubscription('regress_testsub');
+ to_regsubscription 
+--------------------
+ regress_testsub
+(1 row)
+
+SELECT to_regsubscription('"regress_testsub"');
+ to_regsubscription 
+--------------------
+ regress_testsub
+(1 row)
+
 /* If objects don't exist, raise errors. */
 DROP ROLE regress_regrole_test;
 DROP PUBLICATION regress_testpub;
+ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
+DROP SUBSCRIPTION regress_testsub;
 -- without schemaname
 SELECT regoper('||//');
 ERROR:  operator does not exist: ||//
@@ -346,6 +373,18 @@ SELECT regpublication('foo.bar');
 ERROR:  invalid name syntax
 LINE 1: SELECT regpublication('foo.bar');
                               ^
+SELECT regsubscription('Nonexistent');
+ERROR:  subscription "nonexistent" does not exist
+LINE 1: SELECT regsubscription('Nonexistent');
+                               ^
+SELECT regsubscription('"Nonexistent"');
+ERROR:  subscription "Nonexistent" does not exist
+LINE 1: SELECT regsubscription('"Nonexistent"');
+                               ^
+SELECT regsubscription('foo.bar');
+ERROR:  invalid name syntax
+LINE 1: SELECT regsubscription('foo.bar');
+                               ^
 /* If objects don't exist, return NULL with no error. */
 -- without schemaname
 SELECT to_regoper('||//');
@@ -490,3 +529,17 @@ SELECT to_regpublication('"Nonexistent"');
 
 SELECT to_regpublication('foo.bar');
 ERROR:  invalid name syntax
+SELECT to_regsubscription('Nonexistent');
+ to_regsubscription 
+--------------------
+ 
+(1 row)
+
+SELECT to_regsubscription('"Nonexistent"');
+ to_regsubscription 
+--------------------
+ 
+(1 row)
+
+SELECT to_regsubscription('foo.bar');
+ERROR:  invalid name syntax
diff --git a/src/test/regress/sql/regproc.sql b/src/test/regress/sql/regproc.sql
index df29cfd3e8..19a17b946a 100644
--- a/src/test/regress/sql/regproc.sql
+++ b/src/test/regress/sql/regproc.sql
@@ -9,6 +9,7 @@ CREATE ROLE regress_regrole_test;
 -- suppress warning that depends on wal_level
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION regress_testpub;
+CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false);
 RESET client_min_messages;
 
 -- without schemaname
@@ -54,6 +55,8 @@ SELECT regnamespace('pg_catalog');
 SELECT regnamespace('"pg_catalog"');
 SELECT regpublication('regress_testpub');
 SELECT regpublication('"regress_testpub"');
+SELECT regsubscription('regress_testsub');
+SELECT regsubscription('"regress_testsub"');
 
 SELECT to_regrole('regress_regrole_test');
 SELECT to_regrole('"regress_regrole_test"');
@@ -61,11 +64,15 @@ SELECT to_regnamespace('pg_catalog');
 SELECT to_regnamespace('"pg_catalog"');
 SELECT to_regpublication('regress_testpub');
 SELECT to_regpublication('"regress_testpub"');
+SELECT to_regsubscription('regress_testsub');
+SELECT to_regsubscription('"regress_testsub"');
 
 /* If objects don't exist, raise errors. */
 
 DROP ROLE regress_regrole_test;
 DROP PUBLICATION regress_testpub;
+ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
+DROP SUBSCRIPTION regress_testsub;
 
 -- without schemaname
 
@@ -99,6 +106,9 @@ SELECT regnamespace('foo.bar');
 SELECT regpublication('Nonexistent');
 SELECT regpublication('"Nonexistent"');
 SELECT regpublication('foo.bar');
+SELECT regsubscription('Nonexistent');
+SELECT regsubscription('"Nonexistent"');
+SELECT regsubscription('foo.bar');
 
 /* If objects don't exist, return NULL with no error. */
 
@@ -136,3 +146,6 @@ SELECT to_regnamespace('foo.bar');
 SELECT to_regpublication('Nonexistent');
 SELECT to_regpublication('"Nonexistent"');
 SELECT to_regpublication('foo.bar');
+SELECT to_regsubscription('Nonexistent');
+SELECT to_regsubscription('"Nonexistent"');
+SELECT to_regsubscription('foo.bar');
-- 
2.25.1

