From d2986b4f1735b8e18c17abc91eb6fe349262d8c9 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 25 Apr 2014 17:54:19 -0300
Subject: [PATCH 11/29] deparse: Support CREATE/ALTER DOMAIN

---
 src/backend/tcop/deparse_utility.c | 140 ++++++++++++++++++++++++++++++++++++-
 src/backend/utils/adt/ruleutils.c  |  23 ++++++
 src/include/utils/ruleutils.h      |   2 +
 3 files changed, 163 insertions(+), 2 deletions(-)

diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index c9a89b5..d5a4d05 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -834,6 +834,88 @@ deparse_AlterExtensionContentsStmt(Oid objectId, Node *parsetree,
 	return stmt;
 }
 
+static ObjTree *
+deparse_AlterDomainStmt(Oid objectId, Node *parsetree,
+						ObjectAddress constraintAddr)
+{
+	AlterDomainStmt *node = (AlterDomainStmt *) parsetree;
+	HeapTuple	domTup;
+	Form_pg_type domForm;
+	ObjTree	   *alterDom;
+	char	   *fmt;
+
+	/* ALTER DOMAIN DROP CONSTRAINT is handled by the DROP support code */
+	if (node->subtype == 'X')
+		return NULL;
+
+	domTup = SearchSysCache1(TYPEOID, objectId);
+	if (!HeapTupleIsValid(domTup))
+		elog(ERROR, "cache lookup failed for domain with OID %u",
+			 objectId);
+	domForm = (Form_pg_type) GETSTRUCT(domTup);
+
+	switch (node->subtype)
+	{
+		case 'T':
+			/* SET DEFAULT / DROP DEFAULT */
+			if (node->def == NULL)
+				fmt = "ALTER DOMAIN %{identity}D DROP DEFAULT";
+			else
+				fmt = "ALTER DOMAIN %{identity}D SET DEFAULT %{default}s";
+			break;
+		case 'N':
+			/* DROP NOT NULL */
+			fmt = "ALTER DOMAIN %{identity}D DROP NOT NULL";
+			break;
+		case 'O':
+			/* SET NOT NULL */
+			fmt = "ALTER DOMAIN %{identity}D SET NOT NULL";
+			break;
+		case 'C':
+			/* ADD CONSTRAINT.  Only CHECK constraints are supported by domains */
+			fmt = "ALTER DOMAIN %{identity}D ADD CONSTRAINT %{constraint_name}I %{definition}s";
+			break;
+		case 'V':
+			/* VALIDATE CONSTRAINT */
+			fmt = "ALTER DOMAIN %{identity}D VALIDATE CONSTRAINT %{constraint_name}I";
+			break;
+		default:
+			elog(ERROR, "invalid subtype %c", node->subtype);
+	}
+
+	alterDom = new_objtree_VA(fmt, 0);
+	append_object_object(alterDom, "identity",
+						 new_objtree_for_qualname(domForm->typnamespace,
+												  NameStr(domForm->typname)));
+
+	/*
+	 * Process subtype-specific options.  Validating a constraint
+	 * requires its name ...
+	 */
+	if (node->subtype == 'V')
+		append_string_object(alterDom, "constraint_name", node->name);
+
+	/* ... a new constraint has a name and definition ... */
+	if (node->subtype == 'C')
+	{
+		append_string_object(alterDom, "definition",
+							 pg_get_constraintdef_string(constraintAddr.objectId,
+														 false));
+		/* can't rely on node->name here; might not be defined */
+		append_string_object(alterDom, "constraint_name",
+							 get_constraint_name(constraintAddr.objectId));
+	}
+
+	/* ... and setting a default has a definition only. */
+	if (node->subtype == 'T' && node->def != NULL)
+		append_string_object(alterDom, "default", DomainGetDefault(domTup));
+
+	/* done */
+	ReleaseSysCache(domTup);
+
+	return alterDom;
+}
+
 /*
  * deparse_CreateTrigStmt
  *		Deparse a CreateTrigStmt (CREATE TRIGGER)
@@ -1795,6 +1877,59 @@ deparse_CreateRangeStmt(Oid objectId, Node *parsetree)
 	return range;
 }
 
+static ObjTree *
+deparse_CreateDomain(Oid objectId, Node *parsetree)
+{
+	ObjTree	   *createDomain;
+	ObjTree	   *tmp;
+	HeapTuple	typTup;
+	Form_pg_type typForm;
+	List	   *constraints;
+
+	typTup = SearchSysCache1(TYPEOID,
+							 objectId);
+	if (!HeapTupleIsValid(typTup))
+		elog(ERROR, "cache lookup failed for domain with OID %u", objectId);
+	typForm = (Form_pg_type) GETSTRUCT(typTup);
+
+	createDomain = new_objtree_VA("CREATE DOMAIN %{identity}D AS %{type}T %{not_null}s %{constraints}s %{collation}s",
+								  0);
+
+	append_object_object(createDomain,
+						 "identity",
+						 new_objtree_for_qualname_id(TypeRelationId,
+													 objectId));
+	append_object_object(createDomain,
+						 "type",
+						 new_objtree_for_type(typForm->typbasetype, typForm->typtypmod));
+
+	if (typForm->typnotnull)
+		append_string_object(createDomain, "not_null", "NOT NULL");
+	else
+		append_string_object(createDomain, "not_null", "");
+
+	constraints = obtainConstraints(NIL, InvalidOid, objectId);
+	tmp = new_objtree_VA("%{elements: }s", 0);
+	if (constraints == NIL)
+		append_bool_object(tmp, "present", false);
+	else
+		append_array_object(tmp, "elements", constraints);
+	append_object_object(createDomain, "constraints", tmp);
+
+	tmp = new_objtree_VA("COLLATE %{collation}D", 0);
+	if (OidIsValid(typForm->typcollation))
+		append_object_object(tmp, "collation",
+							 new_objtree_for_qualname_id(CollationRelationId,
+														 typForm->typcollation));
+	else
+		append_bool_object(tmp, "present", false);
+	append_object_object(createDomain, "collation", tmp);
+
+	ReleaseSysCache(typTup);
+
+	return createDomain;
+}
+
 /*
  * Return the given object type as a string.
  */
@@ -2827,7 +2962,8 @@ deparse_simple_command(StashedCommand *cmd)
 			break;
 
 		case T_AlterDomainStmt:
-			elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+			command = deparse_AlterDomainStmt(objectId, parsetree,
+											  cmd->d.simple.secondaryObject);
 			break;
 
 			/* other local objects */
@@ -2944,7 +3080,7 @@ deparse_simple_command(StashedCommand *cmd)
 			break;
 
 		case T_CreateDomainStmt:
-			elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+			command = deparse_CreateDomain(objectId, parsetree);
 			break;
 
 		case T_CreateConversionStmt:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 169119c..791673a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9728,3 +9728,26 @@ RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext)
 
 	return defstr;
 }
+
+/*
+ * Return the default value of a domain.
+ */
+char *
+DomainGetDefault(HeapTuple domTup)
+{
+	Datum	def;
+	Node   *defval;
+	char   *defstr;
+	bool	isnull;
+
+	def = SysCacheGetAttr(TYPEOID, domTup, Anum_pg_type_typdefaultbin,
+							 &isnull);
+	if (isnull)
+		elog(ERROR, "domain \"%s\" does not have a default value",
+			 NameStr(((Form_pg_type) GETSTRUCT(domTup))->typname));
+	defval = stringToNode(TextDatumGetCString(def));
+	defstr = deparse_expression_pretty(defval, NULL /* dpcontext? */,
+									   false, false, 0, 0);
+
+	return defstr;
+}
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9989cdd..828c80b 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -46,4 +46,6 @@ extern char *generate_collation_name(Oid collid);
 extern char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
 						 List *dpcontext);
 
+extern char *DomainGetDefault(HeapTuple domTup);
+
 #endif	/* RULEUTILS_H */
-- 
2.1.4

