From a745c91edbb0b59fddb4e7efb135f997c7830a2b Mon Sep 17 00:00:00 2001
From: Mikhail Titov <mlt@gmx.us>
Date: Wed, 12 Aug 2020 21:07:22 -0500
Subject: [PATCH v2 2/2] Allow DEFAULT in VALUES RTE for generated columns

Use T_SetToDefault in TLE instead of T_Var during parse analysis
as a marker for all DEFAULTs column. Let existing code to get rid of
that SetToDefault from TLE in INSERT rewriter, then force NULLs
during RTE column rewriting when attribute number was not set
as it normally would with Var.

Original patch was sent as text/x-patch and suffered whitespace mangling.

https://www.postgresql.org/message-id/flat/9q0sgcr416t.fsf@gmx.us
---
 src/backend/parser/parse_relation.c  | 50 ++++++++++++++++++++++------
 src/backend/rewrite/rewriteHandler.c | 13 +++++++-
 2 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index b875a50646..baf05e2185 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -2981,19 +2981,47 @@ expandNSItemVars(ParseNamespaceItem *nsitem,
 		if (colname[0])
 		{
 			Var		   *var;
+			ListCell   *lc2;
+			Node	   *std = NULL;
+			bool		all_default = true;
 
 			Assert(nscol->p_varno > 0);
-			var = makeVar(nscol->p_varno,
-						  nscol->p_varattno,
-						  nscol->p_vartype,
-						  nscol->p_vartypmod,
-						  nscol->p_varcollid,
-						  sublevels_up);
-			/* makeVar doesn't offer parameters for these, so set by hand: */
-			var->varnosyn = nscol->p_varnosyn;
-			var->varattnosyn = nscol->p_varattnosyn;
-			var->location = location;
-			result = lappend(result, var);
+			foreach(lc2, nsitem->p_rte->values_lists)
+			{
+				List *row = (List*) lfirst(lc2);
+				std = list_nth_node(Node, row, colindex);
+				if (!IsA(std, SetToDefault))
+				{
+					all_default = false;
+					break;
+				}
+			}
+
+			if (all_default && std)
+			{
+				/*
+				 * If all nodes for the column are SetToDefault, keep the marker
+				 * for later INSERT VALUES list rewriting where we remove
+				 * TLE and force NULL constant values node while rewriting VALUES RTE.
+				 * We cannot just remove values column
+				 * as attribute numbering will be off.
+				 */
+				result = lappend(result, std);
+			}
+			else
+			{
+				var = makeVar(nscol->p_varno,
+							  nscol->p_varattno,
+							  nscol->p_vartype,
+							  nscol->p_vartypmod,
+							  nscol->p_varcollid,
+							  sublevels_up);
+				/* makeVar doesn't offer parameters for these, so set by hand: */
+				var->varnosyn = nscol->p_varnosyn;
+				var->varattnosyn = nscol->p_varattnosyn;
+				var->location = location;
+				result = lappend(result, var);
+			}
 			if (colnames)
 				*colnames = lappend(*colnames, colnameval);
 		}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index fe777c3103..f885fe7405 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1373,8 +1373,19 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
 				Form_pg_attribute att_tup;
 				Node	   *new_expr;
 
+				/*
+				 * TLE was likely already removed for this generated column.
+				 * We do not have a good way to check it now,
+				 * but attrno should not be 0 anyway.
+				 */
 				if (attrno == 0)
-					elog(ERROR, "cannot set value in column %d to DEFAULT", i);
+				{
+					SetToDefault *std = (SetToDefault*) col;
+					new_expr = (Node*) makeNullConst(std->typeId, std->typeMod, std->collation);
+					newList = lappend(newList, new_expr);
+					continue;
+				}
+
 				att_tup = TupleDescAttr(target_relation->rd_att, attrno - 1);
 
 				if (!force_nulls && !att_tup->attisdropped)
-- 
2.28.0.windows.1

