>From d29332d1e91f9016278abfda90372e8118e47056 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 9 Feb 2015 15:38:07 -0300
Subject: [PATCH 30/42] deparse: support ALTER DOMAIN

---
 src/backend/commands/typecmds.c    | 70 +++++++++++++++++---------------
 src/backend/tcop/deparse_utility.c | 83 +++++++++++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c         |  5 ++-
 src/backend/utils/adt/ruleutils.c  | 20 +++++++++
 src/include/commands/typecmds.h    |  2 +-
 src/include/utils/ruleutils.h      |  2 +
 6 files changed, 146 insertions(+), 36 deletions(-)

diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 0564535..0fd225c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -108,7 +108,7 @@ static void checkEnumOwner(HeapTuple tup);
 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
 					Oid baseTypeOid,
 					int typMod, Constraint *constr,
-					char *domainName);
+					char *domainName, Oid *constrOid);
 
 
 /*
@@ -1075,7 +1075,7 @@ DefineDomain(CreateDomainStmt *stmt)
 			case CONSTR_CHECK:
 				domainAddConstraint(domainoid, domainNamespace,
 									basetypeoid, basetypeMod,
-									constr, domainName);
+									constr, domainName, NULL);
 				break;
 
 				/* Other constraint types were fully processed above */
@@ -2462,7 +2462,7 @@ AlterDomainDropConstraint(List *names, const char *constrName,
  * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
  */
 Oid
-AlterDomainAddConstraint(List *names, Node *newConstraint)
+AlterDomainAddConstraint(List *names, Node *newConstraint, Oid *constrOid)
 {
 	TypeName   *typename;
 	Oid			domainoid;
@@ -2546,7 +2546,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 
 	ccbin = domainAddConstraint(domainoid, typTup->typnamespace,
 								typTup->typbasetype, typTup->typtypmod,
-								constr, NameStr(typTup->typname));
+								constr, NameStr(typTup->typname), constrOid);
 
 	/*
 	 * If requested to validate the constraint, test all values stored in the
@@ -2960,13 +2960,14 @@ checkDomainOwner(HeapTuple tup)
 static char *
 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 					int typMod, Constraint *constr,
-					char *domainName)
+					char *domainName, Oid *constrOid)
 {
 	Node	   *expr;
 	char	   *ccsrc;
 	char	   *ccbin;
 	ParseState *pstate;
 	CoerceToDomainValue *domVal;
+	Oid			ccoid;
 
 	/*
 	 * Assign or validate constraint name
@@ -3045,34 +3046,37 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 	/*
 	 * Store the constraint in pg_constraint
 	 */
-	CreateConstraintEntry(constr->conname,		/* Constraint Name */
-						  domainNamespace,		/* namespace */
-						  CONSTRAINT_CHECK,		/* Constraint Type */
-						  false,	/* Is Deferrable */
-						  false,	/* Is Deferred */
-						  !constr->skip_validation,		/* Is Validated */
-						  InvalidOid,	/* not a relation constraint */
-						  NULL,
-						  0,
-						  domainOid,	/* domain constraint */
-						  InvalidOid,	/* no associated index */
-						  InvalidOid,	/* Foreign key fields */
-						  NULL,
-						  NULL,
-						  NULL,
-						  NULL,
-						  0,
-						  ' ',
-						  ' ',
-						  ' ',
-						  NULL, /* not an exclusion constraint */
-						  expr, /* Tree form of check constraint */
-						  ccbin,	/* Binary form of check constraint */
-						  ccsrc,	/* Source form of check constraint */
-						  true, /* is local */
-						  0,	/* inhcount */
-						  false,	/* connoinherit */
-						  false);		/* is_internal */
+	ccoid =
+		CreateConstraintEntry(constr->conname,		/* Constraint Name */
+							  domainNamespace,		/* namespace */
+							  CONSTRAINT_CHECK,		/* Constraint Type */
+							  false,	/* Is Deferrable */
+							  false,	/* Is Deferred */
+							  !constr->skip_validation,		/* Is Validated */
+							  InvalidOid,	/* not a relation constraint */
+							  NULL,
+							  0,
+							  domainOid,	/* domain constraint */
+							  InvalidOid,	/* no associated index */
+							  InvalidOid,	/* Foreign key fields */
+							  NULL,
+							  NULL,
+							  NULL,
+							  NULL,
+							  0,
+							  ' ',
+							  ' ',
+							  ' ',
+							  NULL, /* not an exclusion constraint */
+							  expr, /* Tree form of check constraint */
+							  ccbin,	/* Binary form of check constraint */
+							  ccsrc,	/* Source form of check constraint */
+							  true, /* is local */
+							  0,	/* inhcount */
+							  false,	/* connoinherit */
+							  false);		/* is_internal */
+	if (constrOid)
+		*constrOid = ccoid;
 
 	/*
 	 * Return the compiled constraint expression so the calling routine can
diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index bd0a4df..b5b6727 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -1602,6 +1602,86 @@ deparse_CreateExtensionStmt(Oid objectId, Node *parsetree)
 }
 
 static ObjTree *
+deparse_AlterDomainStmt(Oid objectId, Node *parsetree, Oid secondaryOid)
+{
+	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(secondaryOid, false));
+		/* can't rely on node->name here; might not be defined */
+		append_string_object(alterDom, "constraint_name",
+							 get_constraint_name(secondaryOid));
+	}
+
+	/* ... 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;
+}
+
+static ObjTree *
 deparse_AlterExtensionStmt(Oid objectId, Node *parsetree)
 {
 	AlterExtensionStmt *node = (AlterExtensionStmt *) parsetree;
@@ -4956,7 +5036,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.secondaryOid);
 			break;
 
 			/* other local objects */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bec5231..20d6565 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1075,6 +1075,8 @@ ProcessUtilitySlow(Node *parsetree,
 				{
 					AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
 
+					secondaryOid = InvalidOid;
+
 					/*
 					 * Some or all of these functions are recursive to cover
 					 * inherited things, so permission checks are done there.
@@ -1104,7 +1106,8 @@ ProcessUtilitySlow(Node *parsetree,
 						case 'C':		/* ADD CONSTRAINT */
 							objectId =
 								AlterDomainAddConstraint(stmt->typeName,
-														 stmt->def);
+														 stmt->def,
+														 &secondaryOid);
 							break;
 						case 'X':		/* DROP CONSTRAINT */
 							objectId =
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c281c38..2c4786f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9700,6 +9700,26 @@ RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext)
 	return defstr;
 }
 
+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;
+}
+
 /*
  * Return the defaults values of arguments to a function, as a list of
  * deparsed expressions.
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index e18a714..b38584c 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -32,7 +32,7 @@ extern Oid	AssignTypeArrayOid(void);
 
 extern Oid	AlterDomainDefault(List *names, Node *defaultRaw);
 extern Oid	AlterDomainNotNull(List *names, bool notNull);
-extern Oid	AlterDomainAddConstraint(List *names, Node *constr);
+extern Oid	AlterDomainAddConstraint(List *names, Node *constr, Oid *constrOid);
 extern Oid	AlterDomainValidateConstraint(List *names, char *constrName);
 extern Oid AlterDomainDropConstraint(List *names, const char *constrName,
 						  DropBehavior behavior, bool missing_ok);
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 91b61f9..7b22ea0 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -48,4 +48,6 @@ extern List *FunctionGetDefaults(text *proargdefaults);
 extern char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
 						 List *dpcontext);
 
+extern char *DomainGetDefault(HeapTuple domTup);
+
 #endif	/* RULEUTILS_H */
-- 
2.1.4

