From 80b255e12f3f6319b4591b9f8d1a13654b2f812d 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/44] deparse: support ALTER DOMAIN

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

diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a2576f9..1de37eb 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 */
@@ -2482,7 +2482,7 @@ AlterDomainDropConstraint(List *names, const char *constrName,
  * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
  */
 ObjectAddress
-AlterDomainAddConstraint(List *names, Node *newConstraint)
+AlterDomainAddConstraint(List *names, Node *newConstraint, Oid *constrOid)
 {
 	TypeName   *typename;
 	Oid			domainoid;
@@ -2567,7 +2567,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
@@ -2990,13 +2990,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
@@ -3075,34 +3076,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 f680733..5e89514 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -1587,6 +1587,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;
@@ -4975,7 +5055,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 9b6e1f4..b45965c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1117,7 +1117,8 @@ ProcessUtilitySlow(Node *parsetree,
 						case 'C':		/* ADD CONSTRAINT */
 							address =
 								AlterDomainAddConstraint(stmt->typeName,
-														 stmt->def);
+														 stmt->def,
+														 &secondaryOid);
 							break;
 						case 'X':		/* DROP CONSTRAINT */
 							address =
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3ec3e5f..ffc8c1b 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 a0082de..a9e1449 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -32,7 +32,8 @@ extern Oid	AssignTypeArrayOid(void);
 
 extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
 extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
-extern ObjectAddress AlterDomainAddConstraint(List *names, Node *constr);
+extern ObjectAddress AlterDomainAddConstraint(List *names, Node *constr,
+						 Oid *constrOid);
 extern ObjectAddress AlterDomainValidateConstraint(List *names, char *constrName);
 extern ObjectAddress 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

