From bedd04a551c30350ebb8da2aa2596d16053b5d15 Mon Sep 17 00:00:00 2001
From: "Thomas G. Lockhart" <lockhart@fourpalms.org>
Date: Fri, 4 Dec 1998 15:34:49 +0000
Subject: [PATCH] Implement CASE expression.

---
 src/backend/executor/execQual.c         |  87 ++++++++++++-
 src/backend/nodes/copyfuncs.c           |  49 +++++++-
 src/backend/nodes/outfuncs.c            |  87 ++++++++++++-
 src/backend/optimizer/plan/createplan.c |   9 +-
 src/backend/optimizer/util/clauses.c    |  41 ++++++-
 src/backend/optimizer/util/tlist.c      |  30 +++--
 src/backend/optimizer/util/var.c        |  29 ++++-
 src/backend/parser/analyze.c            |  25 ++--
 src/backend/parser/gram.y               | 137 ++++++++++++++++++---
 src/backend/parser/keywords.c           |   8 +-
 src/backend/parser/parse_coerce.c       |   4 +-
 src/backend/parser/parse_expr.c         | 156 +++++++++++++++++++++++-
 src/backend/parser/parse_target.c       |  90 +++++++++-----
 src/backend/rewrite/rewriteHandler.c    |  87 +++++++++++--
 src/include/nodes/nodes.h               |   8 +-
 src/include/nodes/parsenodes.h          |  34 +++++-
 src/include/optimizer/clauses.h         |   4 +-
 17 files changed, 777 insertions(+), 108 deletions(-)

diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 6f9148b56d4..d751b2cb6e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.38 1998/11/27 19:52:00 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.39 1998/12/04 15:33:19 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
  *	 NOTES
  *		ExecEvalExpr() and ExecEvalVar() are hotspots.	making these faster
  *		will speed up the entire system.  Unfortunately they are currently
- *		implemented recursively..  Eliminating the recursion is bound to
+ *		implemented recursively.  Eliminating the recursion is bound to
  *		improve the speed of the executor.
  *
  *		ExecTargetList() is used to make tuple projections.  Rather then
@@ -205,7 +205,7 @@ ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull)
  *		variable with respect to given expression context.
  *
  *
- *		As an entry condition, we expect that the the datatype the
+ *		As an entry condition, we expect that the datatype the
  *		plan expects to get (as told by our "variable" argument) is in
  *		fact the datatype of the attribute the plan says to fetch (as
  *		seen in the current context, identified by our "econtext"
@@ -1124,6 +1124,79 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
 	return const_value;
 }
 
+/* ----------------------------------------------------------------
+ *		ExecEvalCase
+ *
+ *		Evaluate a CASE clause. Will have boolean expressions
+ *		inside the WHEN clauses, and will have constants
+ *		for results.
+ *		- thomas 1998-11-09
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
+{
+	List	   *clauses;
+	List	   *clause;
+	CaseWhen   *wclause;
+	Datum		const_value = 0;
+	bool		isDone;
+
+	clauses = caseExpr->args;
+
+	/******************
+	 *	we evaluate each of the WHEN clauses in turn,
+	 *	as soon as one is true we return the corresponding
+	 *	result.	If none are true then we return the value
+	 *	of the default clause, or NULL.
+	 ******************
+	 */
+	foreach(clause, clauses)
+	{
+
+		/******************
+		 *	We don't iterate over sets in the quals, so pass in an isDone
+		 *	flag, but ignore it.
+		 ******************
+		 */
+
+		wclause = lfirst(clause);
+		const_value = ExecEvalExpr((Node *) wclause->expr,
+								   econtext,
+								   isNull,
+								   &isDone);
+
+		/******************
+		 *	 if we have a true test, then we return the result,
+		 *	 since the case statement is satisfied.
+		 ******************
+		 */
+		if (DatumGetInt32(const_value) != 0)
+		{
+			const_value = ExecEvalExpr((Node *) wclause->result,
+									   econtext,
+									   isNull,
+									   &isDone);
+			return (Datum) const_value;
+
+		}
+	}
+
+	if (caseExpr->defresult)
+	{
+		const_value = ExecEvalExpr((Node *) caseExpr->defresult,
+								   econtext,
+								   isNull,
+								   &isDone);
+	}
+	else
+	{
+		*isNull = true;
+	}
+
+	return const_value;
+}
+
 /* ----------------------------------------------------------------
  *		ExecEvalExpr
  *
@@ -1236,13 +1309,18 @@ ExecEvalExpr(Node *expression,
 				}
 				break;
 			}
+		case T_CaseExpr:
+			retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull);
+			break;
+
 		default:
 			elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression));
 			break;
 	}
 
 	return retDatum;
-}
+} /* ExecEvalExpr() */
+
 
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList
@@ -1642,3 +1720,4 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone)
 					   InvalidBuffer,	/* tuple has no buffer */
 					   true);
 }
+
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5809a4044c3..33f7b14894a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.50 1998/11/22 10:48:38 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.51 1998/12/04 15:33:33 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -943,6 +943,47 @@ _copySubLink(SubLink *from)
 	return newnode;
 }
 
+/* ----------------
+ *		_copyCaseExpr
+ * ----------------
+ */
+static CaseExpr *
+_copyCaseExpr(CaseExpr *from)
+{
+	CaseExpr   *newnode = makeNode(CaseExpr);
+
+	/* ----------------
+	 *	copy remainder of node
+	 * ----------------
+	 */
+	newnode->casetype = from->casetype;
+
+	Node_Copy(from, newnode, arg);
+	Node_Copy(from, newnode, args);
+	Node_Copy(from, newnode, defresult);
+
+	return newnode;
+}
+
+/* ----------------
+ *		_copyCaseWhen
+ * ----------------
+ */
+static CaseWhen *
+_copyCaseWhen(CaseWhen *from)
+{
+	CaseWhen   *newnode = makeNode(CaseWhen);
+
+	/* ----------------
+	 *	copy remainder of node
+	 * ----------------
+	 */
+	Node_Copy(from, newnode, expr);
+	Node_Copy(from, newnode, result);
+
+	return newnode;
+}
+
 static Array *
 _copyArray(Array *from)
 {
@@ -1734,6 +1775,12 @@ copyObject(void *from)
 		case T_SubLink:
 			retval = _copySubLink(from);
 			break;
+		case T_CaseExpr:
+			retval = _copyCaseExpr(from);
+			break;
+		case T_CaseWhen:
+			retval = _copyCaseWhen(from);
+			break;
 
 			/*
 			 * RELATION NODES
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 33bba59d65b..2cbec8e615b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.48 1998/11/22 10:48:39 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.49 1998/12/04 15:33:33 thomas Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1635,6 +1635,82 @@ _outAConst(StringInfo str, A_Const *node)
 	return;
 }
 
+static void
+_outConstraint(StringInfo str, Constraint *node)
+{
+	char		buf[500];
+
+	sprintf(buf," %s :type",
+		((node->name != NULL)? node->name: "<>"));
+	appendStringInfo(str, buf);
+
+	switch (node->contype)
+	{
+		case CONSTR_PRIMARY:
+			sprintf(buf," PRIMARY KEY ");
+			appendStringInfo(str, buf);
+			_outNode(str, node->keys);
+			break;
+
+		case CONSTR_CHECK:
+			sprintf(buf," CHECK ");
+			appendStringInfo(str, buf);
+			appendStringInfo(str, node->def);
+			break;
+
+		case CONSTR_DEFAULT:
+			sprintf(buf," DEFAULT ");
+			appendStringInfo(str, buf);
+			appendStringInfo(str, node->def);
+			break;
+
+		case CONSTR_NOTNULL:
+			sprintf(buf," NOT NULL ");
+			appendStringInfo(str, buf);
+			break;
+
+		case CONSTR_UNIQUE:
+			sprintf(buf," UNIQUE ");
+			appendStringInfo(str, buf);
+			_outNode(str, node->keys);
+			break;
+
+		default:
+			sprintf(buf,"<unrecognized constraint>");
+			appendStringInfo(str, buf);
+			break;
+	}
+	return;
+}
+
+static void
+_outCaseExpr(StringInfo str, CaseExpr *node)
+{
+	char		buf[500];
+
+	sprintf(buf, "CASE ");
+	appendStringInfo(str, buf);
+	_outNode(str, node->args);
+	sprintf(buf, " :default ");
+	appendStringInfo(str, buf);
+	_outNode(str, node->defresult);
+	return;
+}
+
+static void
+_outCaseWhen(StringInfo str, CaseWhen *node)
+{
+	char		buf[500];
+
+	sprintf(buf, " :when ");
+	appendStringInfo(str, buf);
+	_outNode(str, node->expr);
+	sprintf(buf, " :then ");
+	appendStringInfo(str, buf);
+	_outNode(str, node->result);
+	return;
+}
+
 /*
  * _outNode -
  *	  converts a Node into ascii string and append it to 'str'
@@ -1861,6 +1937,15 @@ _outNode(StringInfo str, void *obj)
 			case T_A_Const:
 				_outAConst(str, obj);
 				break;
+			case T_Constraint:
+				_outConstraint(str, obj);
+				break;
+			case T_CaseExpr:
+				_outCaseExpr(str, obj);
+				break;
+			case T_CaseWhen:
+				_outCaseWhen(str, obj);
+				break;
 			default:
 				elog(NOTICE, "_outNode: don't know how to print type %d ",
 					 nodeTag(obj));
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ccde0f81ea9..af1e1275a04 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.33 1998/11/22 10:48:43 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.34 1998/12/04 15:34:05 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -437,7 +437,7 @@ create_nestloop_node(JoinPath *best_path,
 		 * used in the inner scan path, so we need only consider the first
 		 * set of qualifications in indxqual.
 		 *
-		 * But there may be more than one clauses in this "first set" in the
+		 * But there may be more than one clause in this "first set" in the
 		 * case of multi-column indices. - vadim 03/18/97
 		 */
 
@@ -735,6 +735,11 @@ fix_indxqual_references(Node *clause, Path *index_path)
 		else
 			return clause;
 	}
+	else if (IsA(clause, CaseExpr))
+	{
+		elog(NOTICE,"optimizer: fix_indxqual_references sees CaseExpr");
+		return clause;
+	}
 	else
 	{
 		List	   *oldclauses = (List *) clause;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 892a2d45c35..dfacc55518f 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.25 1998/11/27 19:52:07 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.26 1998/12/04 15:34:14 thomas Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -306,6 +306,26 @@ make_andclause(List *andclauses)
 	return expr;
 }
 
+
+/*****************************************************************************
+ *		CASE clause functions
+ *****************************************************************************/
+
+
+/*
+ * case_clause--
+ *
+ * Returns t iff its argument is a 'case' clause: (CASE { expr }).
+ *
+ */
+bool
+case_clause(Node *clause)
+{
+	return
+	(clause != NULL &&
+	 nodeTag(clause) == T_CaseExpr);
+}
+
 /*****************************************************************************
  *																			 *
  *																			 *
@@ -540,6 +560,19 @@ fix_opid(Node *clause)
 			fix_opid((Node *) get_leftop((Expr *) lfirst(lst)));
 		}
 	}
+	else if (case_clause(clause))
+	{
+		List	   *lst;
+
+		fix_opid(((CaseExpr *) clause)->defresult);
+
+		/* Run through the WHEN clauses... */
+		foreach(lst, ((CaseExpr *) clause)->args)
+		{
+			fix_opid(((CaseWhen *) lfirst(lst))->expr);
+			fix_opid(((CaseWhen *) lfirst(lst))->result);
+		}
+	}
 
 }
 
@@ -577,13 +610,12 @@ fix_opids(List *clauses)
  *		returned for the value if it is unknown or null.
  * END OF OLD OBSOLETE COMMENT.
  * NEW COMMENT:
- * when defining rules one of the attibutes of the operator can
+ * when defining rules one of the attributes of the operator can
  * be a Param node (which is supposed to be treated as a constant).
  * However as there is no value specified for a parameter until run time
- * this routine used to return "" as value, which made 'compute_selec'
+ * this routine used to return "" as value, which caused 'compute_selec'
  * to bomb (because it was expecting a lisp integer and got back a lisp
  * string). Now the code returns a plain old good "lispInteger(0)".
- *
  */
 void
 get_relattval(Node *clause,
@@ -787,3 +819,4 @@ CommuteClause(Node *clause)
 	lfirst(((Expr *) clause)->args) = lsecond(((Expr *) clause)->args);
 	lsecond(((Expr *) clause)->args) = temp;
 }
+
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index dba994423fa..66c52404ec5 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.20 1998/09/22 21:48:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.21 1998/12/04 15:34:15 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -482,7 +482,6 @@ flatten_tlistentry(Node *tlistentry, List *flat_tlist)
 							flatten_tlistentry(lfirst(elt), flat_tlist));
 
 		return ((Node *) make_funcclause((Func *) expr->oper, temp_result));
-
 	}
 	else if (IsA(tlistentry, Aggreg))
 	{
@@ -509,19 +508,36 @@ flatten_tlistentry(Node *tlistentry, List *flat_tlist)
 
 		return tlistentry;
 	}
+	else if (case_clause(tlistentry))
+	{
+		CaseExpr   *cexpr = (CaseExpr *) tlistentry;
+		CaseWhen   *cwhen;
+		List	   *elt = NIL;
+
+		foreach(elt, cexpr->args)
+			cwhen = (CaseWhen *)lfirst(elt);
+			cwhen->result = flatten_tlistentry(cwhen->result, flat_tlist);
+		cexpr->defresult = flatten_tlistentry(cexpr->defresult, flat_tlist);
+
+		return ((Node *) cexpr);
+	}
 	else
 	{
-		Expr	   *expr = (Expr *) tlistentry;
+		Expr	   *expr, *final;
+		Var 	   *left, *right;
+
+		Assert(IsA(tlistentry, Expr));
 
-		Var 	   *left = (Var *) flatten_tlistentry((Node *) get_leftop(expr),
+		expr = (Expr *) tlistentry;
+		left = (Var *) flatten_tlistentry((Node *) get_leftop(expr),
 								   flat_tlist);
-		Var		   *right = (Var *) flatten_tlistentry((Node *) get_rightop(expr),
+		right = (Var *) flatten_tlistentry((Node *) get_rightop(expr),
 								   flat_tlist);
-		Expr	   *final = make_opclause((Oper *) expr->oper, left, right);
 
-		Assert(IsA(tlistentry, Expr));
+		final = make_opclause((Oper *) expr->oper, left, right);
 		final->opType = expr->opType;
 		final->typeOid = expr->typeOid;
+
 		return (Node *)final;
 	}
 }
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 4f7b0b41b4d..3dade59a5ca 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.13 1998/09/01 03:24:00 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.14 1998/12/04 15:34:15 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,6 +137,21 @@ contain_var_clause(Node *clause)
 	else if (is_opclause(clause))
 		return (contain_var_clause((Node *) get_leftop((Expr *) clause)) ||
 			  contain_var_clause((Node *) get_rightop((Expr *) clause)));
+	else if (case_clause(clause))
+	{
+		List	   *args;
+		CaseWhen   *when;
+
+		foreach(args, ((CaseExpr *) clause)->args)
+		{
+			when = lfirst(args);
+			if (contain_var_clause(when->expr))
+				return TRUE;
+			if (contain_var_clause(when->result))
+				return TRUE;
+		}
+		return (contain_var_clause(((CaseExpr *) clause)->defresult));
+	}
 
 	return FALSE;
 }
@@ -199,6 +214,18 @@ pull_var_clause(Node *clause)
 	else if (is_opclause(clause))
 		retval = nconc(pull_var_clause((Node *) get_leftop((Expr *) clause)),
 				 pull_var_clause((Node *) get_rightop((Expr *) clause)));
+	else if (case_clause(clause))
+	{
+		List	   *temp;
+
+		foreach(temp, ((CaseExpr *) clause)->args)
+		{
+			retval = nconc(retval, pull_var_clause(((CaseWhen *) lfirst(temp))->expr));
+			retval = nconc(retval, pull_var_clause(((CaseWhen *) lfirst(temp))->result));
+		}
+
+		retval = nconc(retval, pull_var_clause(((CaseExpr *) clause)->defresult));
+	}
 	else
 		retval = NIL;
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index f3e3f9e0783..0a692bd5060 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.89 1998/10/28 16:06:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.90 1998/12/04 15:34:28 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -396,7 +396,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	 */
 	if ((qry->hasAggs == false) && (qry->havingQual != NULL))
 	{
-		elog(ERROR, "This is not a valid having query!");
+		elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
 		return (Query *) NIL;
 	}
 
@@ -621,7 +621,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 								break;
 
 							default:
-								elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
+								elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
 								break;
 						}
 						clist = lnext(clist);
@@ -653,16 +653,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 
 					case CONSTR_NOTNULL:
 					case CONSTR_DEFAULT:
-						elog(ERROR, "parser: internal error; illegal context for constraint", NULL);
+						elog(ERROR, "parser: illegal context for constraint (internal error)", NULL);
 						break;
 					default:
-						elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
+						elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
 						break;
 				}
 				break;
 
 			default:
-				elog(ERROR, "parser: internal error; unrecognized node", NULL);
+				elog(ERROR, "parser: unrecognized node (internal error)", NULL);
 		}
 
 		elements = lnext(elements);
@@ -684,7 +684,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 	{
 		constraint = lfirst(dlist);
 		if (nodeTag(constraint) != T_Constraint)
-			elog(ERROR, "parser: internal error; unrecognized deferred node", NULL);
+			elog(ERROR, "parser: unrecognized deferred node (internal error)", NULL);
 
 		if (constraint->contype == CONSTR_PRIMARY)
 		{
@@ -695,7 +695,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 				have_pkey = TRUE;
 		}
 		else if (constraint->contype != CONSTR_UNIQUE)
-			elog(ERROR, "parser: internal error; unrecognized deferred constraint", NULL);
+			elog(ERROR, "parser: unrecognized deferred constraint (internal error)", NULL);
 
 		index = makeNode(IndexStmt);
 
@@ -735,7 +735,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 				columns = lnext(columns);
 			}
 			if (column == NULL)
-				elog(ERROR, "parser: column '%s' in key does not exist", key->name);
+				elog(ERROR, "CREATE TABLE column '%s' in key does not exist", key->name);
 
 			if (constraint->contype == CONSTR_PRIMARY)
 				column->is_not_null = TRUE;
@@ -753,7 +753,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 		}
 
 		if (index->idxname == NULL)
-			elog(ERROR, "parser: unable to construct implicit index for table %s"
+			elog(ERROR, "CREATE TABLE unable to construct implicit index for table %s"
 				 "; name too long", stmt->relname);
 		else
 			elog(NOTICE, "CREATE TABLE/%s will create implicit index %s for table %s",
@@ -918,8 +918,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	/*
 	 * The havingQual has a similar meaning as "qual" in the where
 	 * statement. So we can easily use the code from the "where clause"
-	 * with some additional traversals done in
-	 * .../optimizer/plan/planner.c
+	 * with some additional traversals done in optimizer/plan/planner.c
 	 */
 	qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
 
@@ -955,7 +954,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	 */
 	if ((qry->hasAggs == false) && (qry->havingQual != NULL))
 	{
-		elog(ERROR, "This is not a valid having query!");
+		elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
 		return (Query *) NIL;
 	}
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d2e34484dac..9382d4a7dcb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.37 1998/10/14 15:56:43 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.38 1998/12/04 15:34:29 thomas Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -208,6 +208,8 @@ Oid	param_type(int t); /* used in parse_expr.c */
 %type <list>	row_descriptor, row_list, c_list, c_expr
 %type <node>	row_expr
 %type <str>		row_op
+%type <node>	case_expr, case_arg, when_clause, case_default
+%type <list>	when_clause_list
 %type <ival>	sub_type
 %type <list>	OptCreateAs, CreateAsList
 %type <node>	CreateAsElement
@@ -259,26 +261,27 @@ Oid	param_type(int t); /* used in parse_expr.c */
 /* Keywords (in SQL92 reserved words) */
 %token	ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC,
 		BEGIN_TRANS, BETWEEN, BOTH, BY,
-		CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE, COLLATE, COLUMN, COMMIT, 
+		CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE,
+		COALESCE, COLLATE, COLUMN, COMMIT, 
 		CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, 
 		CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
 		DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
-		END_TRANS, EXECUTE, EXISTS, EXTRACT,
+		ELSE, END_TRANS, EXECUTE, EXISTS, EXTRACT,
 		FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
 		GRANT, GROUP, HAVING, HOUR_P,
 		IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS,
 		JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL,
 		MATCH, MINUTE_P, MONTH_P, NAMES,
-		NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC,
+		NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC,
 		OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P,
 		PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC,
 		READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK,
 		SCROLL, SECOND_P, SELECT, SET, SUBSTRING,
-		TABLE, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
+		TABLE, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
 		TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
 		UNION, UNIQUE, UPDATE, USER, USING,
 		VALUES, VARCHAR, VARYING, VIEW,
-		WHERE, WITH, WORK, YEAR_P, ZONE
+		WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
 
 /* Keywords (in SQL3 reserved words) */
 %token	TRIGGER
@@ -2861,7 +2864,7 @@ opt_array_bounds:  '[' ']' nest_array_bounds
 				{  $$ = lcons(makeInteger(-1), $3); }
 		| '[' Iconst ']' nest_array_bounds
 				{  $$ = lcons(makeInteger($2), $4); }
-		| /* EMPTY */
+		| /*EMPTY*/
 				{  $$ = NIL; }
 		;
 
@@ -3276,14 +3279,14 @@ sub_type:  ANY								{ $$ = ANY_SUBLINK; }
 		| ALL								{ $$ = ALL_SUBLINK; }
 		;
 
-/*
+/* General expressions
  * This is the heart of the expression syntax.
  * Note that the BETWEEN clause looks similar to a boolean expression
  *  and so we must define b_expr which is almost the same as a_expr
  *  but without the boolean expressions.
- * All operations are allowed in a BETWEEN clause if surrounded by parens.
+ * All operations/expressions are allowed in a BETWEEN clause
+ *  if surrounded by parens.
  */
-
 a_expr:  attr opt_indirection
 				{
 					$1->indirection = $2;
@@ -3895,14 +3898,15 @@ a_expr:  attr opt_indirection
 				{	$$ = makeA_Expr(OR, NULL, $1, $3); }
 		| NOT a_expr
 				{	$$ = makeA_Expr(NOT, NULL, NULL, $2); }
+		| case_expr
+				{	$$ = $1; }
 		;
 
-/*
+/* Restricted expressions
  * b_expr is a subset of the complete expression syntax
  *  defined by a_expr. b_expr is used in BETWEEN clauses
  *  to eliminate parser ambiguities stemming from the AND keyword.
  */
-
 b_expr:  attr opt_indirection
 				{
 					$1->indirection = $2;
@@ -4150,7 +4154,7 @@ opt_indirection:  '[' a_expr ']' opt_indirection
 					ai->uidx = $4;
 					$$ = lcons(ai, $6);
 				}
-		| /* EMPTY */
+		| /*EMPTY*/
 				{	$$ = NIL; }
 		;
 
@@ -4169,7 +4173,7 @@ extract_list:  extract_arg FROM a_expr
 					n->val.val.str = $1;
 					$$ = lappend(lcons((Node *)n,NIL), $3);
 				}
-		| /* EMPTY */
+		| /*EMPTY*/
 				{	$$ = NIL; }
 		;
 
@@ -4180,7 +4184,7 @@ extract_arg:  datetime						{ $$ = $1; }
 
 position_list:  position_expr IN position_expr
 				{	$$ = makeList($3, $1, -1); }
-		| /* EMPTY */
+		| /*EMPTY*/
 				{	$$ = NIL; }
 		;
 
@@ -4314,13 +4318,13 @@ substr_list:  expr_list substr_from substr_for
 				{
 					$$ = nconc(nconc($1,$2),$3);
 				}
-		| /* EMPTY */
+		| /*EMPTY*/
 				{	$$ = NIL; }
 		;
 
 substr_from:  FROM expr_list
 				{	$$ = $2; }
-		| /* EMPTY */
+		| /*EMPTY*/
 				{
 					A_Const *n = makeNode(A_Const);
 					n->val.type = T_Integer;
@@ -4331,7 +4335,7 @@ substr_from:  FROM expr_list
 
 substr_for:  FOR expr_list
 				{	$$ = $2; }
-		| /* EMPTY */
+		| /*EMPTY*/
 				{	$$ = NIL; }
 		;
 
@@ -4379,6 +4383,94 @@ not_in_expr_nodes:  AexprConst
 				}
 		;
 
+/* Case clause
+ * Define SQL92-style case clause.
+ * Allow all four forms described in the standard:
+ * - Full specification
+ *  CASE WHEN a = b THEN c ... ELSE d END
+ * - Implicit argument
+ *  CASE a WHEN b THEN c ... ELSE d END
+ * - Conditional NULL
+ *  NULLIF(x,y)
+ *  same as CASE WHEN x = y THEN NULL ELSE x END
+ * - Conditional substitution from list, use first non-null argument
+ *  COALESCE(a,b,...)
+ * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END
+ * - thomas 1998-11-09
+ */
+case_expr:  CASE case_arg when_clause_list case_default END_TRANS
+				{
+					CaseExpr *c = makeNode(CaseExpr);
+					c->arg = $2;
+					c->args = $3;
+					c->defresult = $4;
+					$$ = (Node *)c;
+				}
+		| NULLIF '(' a_expr ',' a_expr ')'
+				{
+					CaseExpr *c = makeNode(CaseExpr);
+					CaseWhen *w = makeNode(CaseWhen);
+					c->args = lcons(w, NIL);
+					c->defresult = $3;
+					w->expr = makeA_Expr(OP, "=", $3, $5);
+					$$ = (Node *)c;
+
+					elog(NOTICE,"NULLIF() not yet fully implemented");
+				}
+		| COALESCE '(' expr_list ')'
+				{
+					CaseExpr *c = makeNode(CaseExpr);
+					CaseWhen *w;
+					List *l;
+					foreach (l,$3)
+					{
+						w = makeNode(CaseWhen);
+						w->expr = makeA_Expr(NOTNULL, NULL, lfirst(l), NULL);
+						w->result = lfirst(l);
+						c->args = lappend(c->args, w);
+					}
+					$$ = (Node *)c;
+
+					elog(NOTICE,"COALESCE() not yet fully implemented");
+				}
+		;
+
+when_clause_list:  when_clause_list when_clause
+				{ $$ = lappend($1, $2); }
+		| when_clause
+				{ $$ = lcons($1, NIL); }
+		;
+
+when_clause:  WHEN a_expr THEN a_expr_or_null
+				{
+					CaseWhen *w = makeNode(CaseWhen);
+					w->expr = $2;
+					w->result = $4;
+					$$ = (Node *)w;
+				}
+		;
+
+case_default:  ELSE a_expr_or_null				{ $$ = $2; }
+		| /*EMPTY*/								{ $$ = NULL; }
+		;
+
+case_arg:  attr opt_indirection
+				{
+					$1->indirection = $2;
+					$$ = (Node *)$1;
+				}
+		| ColId
+				{
+					/* could be a column name or a relation_name */
+					Ident *n = makeNode(Ident);
+					n->name = $1;
+					n->indirection = NULL;
+					$$ = (Node *)n;
+				}
+		| /*EMPTY*/
+				{	$$ = NULL; }
+		;
+
 attr:  relation_name '.' attrs
 				{
 					$$ = makeNode(Attr);
@@ -4512,7 +4604,7 @@ res_target_el2:  a_expr_or_null AS ColLabel
 		;
 
 opt_id:  ColId									{ $$ = $1; }
-		| /* EMPTY */							{ $$ = NULL; }
+		| /*EMPTY*/								{ $$ = NULL; }
 		;
 
 relation_name:	SpecialRuleRelation
@@ -4723,12 +4815,16 @@ ColLabel:  ColId						{ $$ = $1; }
 		| ABORT_TRANS					{ $$ = "abort"; }
 		| ANALYZE						{ $$ = "analyze"; }
 		| BINARY						{ $$ = "binary"; }
+		| CASE							{ $$ = "case"; }
 		| CLUSTER						{ $$ = "cluster"; }
+		| COALESCE						{ $$ = "coalesce"; }
 		| CONSTRAINT					{ $$ = "constraint"; }
 		| COPY							{ $$ = "copy"; }
 		| CROSS							{ $$ = "cross"; }
 		| CURRENT						{ $$ = "current"; }
 		| DO							{ $$ = "do"; }
+		| ELSE							{ $$ = "else"; }
+		| END_TRANS						{ $$ = "end"; }
 		| EXPLAIN						{ $$ = "explain"; }
 		| EXTEND						{ $$ = "extend"; }
 		| FALSE_P						{ $$ = "false"; }
@@ -4740,6 +4836,7 @@ ColLabel:  ColId						{ $$ = $1; }
 		| MOVE							{ $$ = "move"; }
 		| NEW							{ $$ = "new"; }
 		| NONE							{ $$ = "none"; }
+		| NULLIF						{ $$ = "nullif"; }
 		| ORDER							{ $$ = "order"; }
 		| POSITION						{ $$ = "position"; }
 		| PRECISION						{ $$ = "precision"; }
@@ -4747,10 +4844,12 @@ ColLabel:  ColId						{ $$ = $1; }
 		| SETOF							{ $$ = "setof"; }
 		| SHOW							{ $$ = "show"; }
 		| TABLE							{ $$ = "table"; }
+		| THEN							{ $$ = "then"; }
 		| TRANSACTION					{ $$ = "transaction"; }
 		| TRUE_P						{ $$ = "true"; }
 		| VACUUM						{ $$ = "vacuum"; }
 		| VERBOSE						{ $$ = "verbose"; }
+		| WHEN							{ $$ = "when"; }
 		;
 
 SpecialRuleRelation:  CURRENT
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index eddfd0b3153..aa9a9c329ca 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.48 1998/10/18 23:30:17 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.49 1998/12/04 15:34:29 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,12 +51,14 @@ static ScanKeyword ScanKeywords[] = {
 	{"by", BY},
 	{"cache", CACHE},
 	{"cascade", CASCADE},
+	{"case", CASE},
 	{"cast", CAST},
 	{"char", CHAR},
 	{"character", CHARACTER},
 	{"check", CHECK},
 	{"close", CLOSE},
 	{"cluster", CLUSTER},
+	{"coalesce", COALESCE},
 	{"collate", COLLATE},
 	{"column", COLUMN},
 	{"commit", COMMIT},
@@ -88,6 +90,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"double", DOUBLE},
 	{"drop", DROP},
 	{"each", EACH},
+	{"else", ELSE},
 	{"encoding", ENCODING},
 	{"end", END_TRANS},
 	{"execute", EXECUTE},
@@ -154,6 +157,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"notify", NOTIFY},
 	{"notnull", NOTNULL},
 	{"null", NULL_P},
+	{"nullif", NULLIF},
 	{"numeric", NUMERIC},
 	{"of", OF},
 	{"oids", OIDS},
@@ -201,6 +205,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"stdout", STDOUT},
 	{"substring", SUBSTRING},
 	{"table", TABLE},
+	{"then", THEN},
 	{"time", TIME},
 	{"timestamp", TIMESTAMP},
 	{"timezone_hour", TIMEZONE_HOUR},
@@ -228,6 +233,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"verbose", VERBOSE},
 	{"version", VERSION},
 	{"view", VIEW},
+	{"when", WHEN},
 	{"where", WHERE},
 	{"with", WITH},
 	{"work", WORK},
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 45469226333..4af1f7eafa4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.9 1998/10/22 13:50:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.10 1998/12/04 15:34:30 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -342,7 +342,7 @@ TypeCategory(Oid inType)
 
 
 /* IsPreferredType()
- * Assign a category to the specified OID.
+ * Check if this type is a preferred type.
  */
 bool
 IsPreferredType(CATEGORY category, Oid type)
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 0c2a40a190a..8dcabc48dbb 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.36 1998/10/02 16:23:05 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.37 1998/12/04 15:34:30 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@
 #include "parser/parse_node.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
+#include "parser/parse_coerce.h"
 #include "utils/builtins.h"
 
 static Node *parser_typecast(Value *expr, TypeName *typename, int32 atttypmod);
@@ -265,7 +266,9 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 				foreach(args, fn->args)
 					lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
 				result = ParseFuncOrColumn(pstate,
-						   fn->funcname, fn->args, &pstate->p_last_resno,
+										   fn->funcname,
+										   fn->args,
+										   &pstate->p_last_resno,
 										   precedence);
 				break;
 			}
@@ -332,6 +335,146 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 				break;
 			}
 
+		case T_CaseExpr:
+			{
+				CaseExpr   *c = (CaseExpr *) expr;
+				CaseWhen   *w;
+				List	   *args;
+				Oid			ptype;
+				CATEGORY	pcategory;
+
+				/* transform the list of arguments */
+				foreach(args, c->args)
+				{
+					w = lfirst(args);
+					/* shorthand form was specified, so expand... */
+					if (c->arg != NULL)
+					{
+						A_Expr *a = makeNode(A_Expr);
+						a->oper = OP;
+						a->opname = "=";
+						a->lexpr = c->arg;
+						a->rexpr = w->expr;
+						w->expr = (Node *)a;
+					}
+					lfirst(args) = transformExpr(pstate, (Node *) w, precedence);
+
+					if (w->result == NULL)
+					{
+						A_Const *n = makeNode(A_Const);
+						n->val.type = T_Null;
+						w->result = (Node *)n;
+					}
+				}
+
+				if (c->defresult == NULL)
+				{
+					A_Const *n = makeNode(A_Const);
+					n->val.type = T_Null;
+					c->defresult = (Node *)n;
+				}
+				c->defresult = transformExpr(pstate, (Node *) c->defresult, precedence);
+				c->casetype = exprType(c->defresult);
+
+				/* now check types across result clauses... */
+				ptype = c->casetype;
+				pcategory = TypeCategory(ptype);
+				foreach(args, c->args)
+				{
+					Oid			wtype;
+
+					w = lfirst(args);
+					wtype = exprType(w->result);
+					/* move on to next one if no new information... */
+					if (wtype && (wtype != UNKNOWNOID)
+					 && (wtype != ptype))
+					{
+						/* so far, only nulls so take anything... */
+						if (!ptype)
+						{
+							ptype = wtype;
+							pcategory = TypeCategory(ptype);
+						}
+						/* both types in different categories? then not much hope... */
+						else if ((TypeCategory(wtype) != pcategory)
+							|| ((TypeCategory(wtype) == USER_TYPE)
+							 && (TypeCategory(c->casetype) == USER_TYPE)))
+						{
+							elog(ERROR,"CASE/WHEN types '%s' and '%s' not matched",
+								 typeidTypeName(c->casetype), typeidTypeName(wtype));
+						}
+						/* new one is preferred and can convert? then take it... */
+						else if (IsPreferredType(pcategory, wtype)
+								 && can_coerce_type(1, &ptype, &wtype))
+						{
+							ptype = wtype;
+							pcategory = TypeCategory(ptype);
+						}
+					}
+				}
+
+				/* Convert default clause, if necessary */
+				if (c->casetype != ptype)
+				{
+					if (! c->casetype)
+					{
+						/* default clause is NULL,
+						 * so assign preferred type from WHEN clauses... */
+						c->casetype = ptype;
+					}
+					else if (can_coerce_type(1, &c->casetype, &ptype))
+					{
+						c->defresult = coerce_type(pstate, c->defresult, c->casetype, ptype);
+						c->casetype = ptype;
+					}
+					else
+					{
+						elog(ERROR,"CASE/ELSE unable to convert to type %s",
+							 typeidTypeName(ptype));
+					}
+				}
+
+				/* Convert when clauses, if not null and if necessary */
+				foreach(args, c->args)
+				{
+					Oid		wtype;
+
+					w = lfirst(args);
+					wtype = exprType(w->result);
+					/* only bother with conversion if not NULL and different type... */
+					if (wtype && (wtype != ptype))
+					{
+						if (can_coerce_type(1, &wtype, &ptype))
+						{
+							w->result = coerce_type(pstate, w->result, wtype, ptype);
+						}
+						else
+						{
+							elog(ERROR,"CASE/WHEN unable to convert to type %s",
+								 typeidTypeName(ptype));
+						}
+					}
+				}
+
+				result = expr;
+				break;
+			}
+
+		case T_CaseWhen:
+			{
+				CaseWhen   *w = (CaseWhen *) expr;
+
+				w->expr = transformExpr(pstate, (Node *) w->expr, precedence);
+				if (exprType(w->expr) != BOOLOID)
+					elog(ERROR,"WHEN clause must have a boolean result");
+
+				/* result is NULL for NULLIF() construct - thomas 1998-11-11 */
+				if (w->result != NULL)
+					w->result = transformExpr(pstate, (Node *) w->result, precedence);
+				result = expr;
+				break;
+			}
+
 /* Some nodes do _not_ come from the original parse tree,
  *	but result from parser transformation in this phase.
  * At least one construct (BETWEEN/AND) puts the same nodes
@@ -423,6 +566,9 @@ exprType(Node *expr)
 {
 	Oid			type = (Oid) 0;
 
+	if (!expr)
+		return type;
+
 	switch (nodeTag(expr))
 	{
 		case T_Func:
@@ -452,6 +598,12 @@ exprType(Node *expr)
 		case T_SubLink:
 			type = BOOLOID;
 			break;
+		case T_CaseExpr:
+			type = ((CaseExpr *) expr)->casetype;
+			break;
+		case T_CaseWhen:
+			type = exprType(((CaseWhen *) expr)->result);
+			break;
 		case T_Ident:
 			/* is this right? */
 			type = UNKNOWNOID;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index f05764e765c..e77183387d7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.30 1998/10/08 18:29:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.31 1998/12/04 15:34:30 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,6 +39,9 @@ static Node *SizeTargetExpr(ParseState *pstate,
 			   Oid attrtype,
 			   int32 attrtypmod);
 
+static TargetEntry *
+MakeTargetEntryCase(ParseState *pstate,
+					ResTarget *res);
 
 /* MakeTargetEntryIdent()
  * Transforms an Ident Node to a Target Entry
@@ -53,11 +56,7 @@ static Node *SizeTargetExpr(ParseState *pstate,
  */
 TargetEntry *
 MakeTargetEntryIdent(ParseState *pstate,
-#if FALSE
-					 Ident *ident,
-#else
 					 Node *node,
-#endif
 					 char **resname,
 					 char *refname,
 					 char *colname,
@@ -77,7 +76,7 @@ MakeTargetEntryIdent(ParseState *pstate,
 			pstate->p_insert_columns = lnext(pstate->p_insert_columns);
 		}
 		else
-			elog(ERROR, "insert: more expressions than target columns");
+			elog(ERROR, "INSERT has more expressions than target columns");
 	}
 
 	if (pstate->p_is_insert || pstate->p_is_update)
@@ -105,7 +104,7 @@ MakeTargetEntryIdent(ParseState *pstate,
 		{
 			rte = colnameRangeTableEntry(pstate, colname);
 			if (rte == (RangeTblEntry *) NULL)
-				elog(ERROR, "attribute %s not found", colname);
+				elog(ERROR, "Attribute %s not found", colname);
 			refname = rte->refname;
 		}
 
@@ -129,14 +128,9 @@ MakeTargetEntryIdent(ParseState *pstate,
 			}
 			else
 			{
-#if TRUE
 				elog(ERROR, "Unable to convert %s to %s for column %s",
 					 typeidTypeName(attrtype_id), typeidTypeName(attrtype_target),
 					 target_colname);
-#else
-				elog(ERROR, "Type or size of %s(%d) does not match target column %s(%d)",
-				 colname, attrtypmod, target_colname, attrtypmod_target);
-#endif
 			}
 		}
 	}
@@ -152,11 +146,7 @@ MakeTargetEntryIdent(ParseState *pstate,
 
 		name = ((*resname != NULL) ? *resname : colname);
 
-#if FALSE
-		expr = transformIdent(pstate, (Node *) ident, EXPR_COLUMN_FIRST);
-#else
 		expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-#endif
 
 		attrtype_target = exprType(expr);
 		if (nodeTag(expr) == T_Var)
@@ -187,7 +177,7 @@ MakeTargetEntryIdent(ParseState *pstate,
  * - thomas 1998-05-08
  *
  * Added resjunk flag and made extern so that it can be use by GROUP/
- * ORDER BY a function or expersion not in the target_list
+ * ORDER BY a function or expression not in the target_list
  * -  daveh@insightdist.com 1998-07-31
  */
 TargetEntry *
@@ -207,7 +197,7 @@ MakeTargetEntryExpr(ParseState *pstate,
 	Resdom	   *resnode;
 
 	if (expr == NULL)
-		elog(ERROR, "MakeTargetEntryExpr: invalid use of NULL expression");
+		elog(ERROR, "Invalid use of NULL expression (internal error)");
 
 	type_id = exprType(expr);
 	if (nodeTag(expr) == T_Var)
@@ -251,9 +241,9 @@ MakeTargetEntryExpr(ParseState *pstate,
 				expr = CoerceTargetExpr(pstate, expr, type_id, typelem);
 
 				if (!HeapTupleIsValid(expr))
-					elog(ERROR, "parser: attribute '%s' is of type '%s'"
+					elog(ERROR, "Attribute '%s' is of type '%s'"
 						 " but expression is of type '%s'"
-					"\n\tYou will need to rewrite or cast the expression",
+						 "\n\tYou will need to rewrite or cast the expression",
 						 colname,
 						 typeidTypeName(attrtype),
 						 typeidTypeName(type_id));
@@ -323,6 +313,45 @@ MakeTargetEntryExpr(ParseState *pstate,
 	return makeTargetEntry(resnode, expr);
 }	/* MakeTargetEntryExpr() */
 
+/*
+ *	MakeTargetEntryCase()
+ *	Make a TargetEntry from a case node.
+ */
+static TargetEntry *
+MakeTargetEntryCase(ParseState *pstate,
+					ResTarget *res)
+{
+	TargetEntry	*tent;
+	CaseExpr	*expr;
+	Resdom		*resnode;
+	int			 resdomno;
+	Oid			 type_id;
+	int32		 type_mod;
+
+	expr = (CaseExpr *)transformExpr(pstate, (Node *)res->val, EXPR_COLUMN_FIRST);
+
+	type_id = expr->casetype;
+	type_mod = -1;
+	handleTargetColname(pstate, &res->name, NULL, NULL);
+	if (res->name == NULL)
+		res->name = FigureColname((Node *)expr, res->val);
+
+	resdomno = pstate->p_last_resno++;
+	resnode = makeResdom((AttrNumber) resdomno,
+						 (Oid) type_id,
+						 type_mod,
+						 res->name,
+						 (Index) 0,
+						 (Oid) 0,
+						 0);
+
+	tent = makeNode(TargetEntry);
+	tent->resdom = resnode;
+	tent->expr = (Node *)expr;
+
+	return tent;
+}	/* MakeTargetEntryCase() */
+
 /*
  *	MakeTargetEntryComplex()
  *	Make a TargetEntry from a complex node.
@@ -351,7 +380,7 @@ MakeTargetEntryComplex(ParseState *pstate,
 		Value	   *constval;
 
 		if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
-			elog(ERROR, "yyparse: string constant expected");
+			elog(ERROR, "String constant expected (internal error)");
 
 		val = (char *) textout((struct varlena *)
 							   ((Const *) expr)->constvalue);
@@ -376,7 +405,7 @@ MakeTargetEntryComplex(ParseState *pstate,
 			else
 				lindx[i] = 1;
 			if (lindx[i] > uindx[i])
-				elog(ERROR, "yyparse: lower index cannot be greater than upper index");
+				elog(ERROR, "Lower index cannot be greater than upper index");
 
 			sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
 			str += strlen(str);
@@ -388,7 +417,7 @@ MakeTargetEntryComplex(ParseState *pstate,
 		resdomno = attnameAttNum(rd, res->name);
 		ndims = attnumAttNelems(rd, resdomno);
 		if (i != ndims)
-			elog(ERROR, "yyparse: array dimensions do not match");
+			elog(ERROR, "Array dimensions do not match");
 
 		constval = makeNode(Value);
 		constval->type = T_String;
@@ -400,9 +429,9 @@ MakeTargetEntryComplex(ParseState *pstate,
 	}
 	else
 	{
-		char	   *colname = res->name;
-
 		/* this is not an array assignment */
+		char	  *colname = res->name;
+
 		if (colname == NULL)
 		{
 
@@ -540,6 +569,11 @@ transformTargetList(ParseState *pstate, List *targetlist)
 					tent = MakeTargetEntryComplex(pstate, res);
 					break;
 				}
+			case T_CaseExpr:
+				{
+					tent = MakeTargetEntryCase(pstate, res);
+					break;
+				}
 			case T_Attr:
 				{
 					bool		expand_star = false;
@@ -604,7 +638,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
 				}
 			default:
 				/* internal error */
-				elog(ERROR, "internal error: do not know how to transform targetlist");
+				elog(ERROR, "Unable to transform targetlist (internal error)");
 				break;
 		}
 
@@ -788,7 +822,7 @@ ExpandAllTables(ParseState *pstate)
 
 	/* this should not happen */
 	if (rtable == NULL)
-		elog(ERROR, "cannot expand: null p_rtable");
+		elog(ERROR, "Cannot expand tables; null p_rtable (internal error)");
 
 	/*
 	 * go through the range table and make a list of range table entries
@@ -838,7 +872,7 @@ FigureColname(Node *expr, Node *resval)
 {
 	switch (nodeTag(expr))
 	{
-			case T_Aggreg:
+		case T_Aggreg:
 			return (char *) ((Aggreg *) expr)->aggname;
 		case T_Expr:
 			if (((Expr *) expr)->opType == FUNC_EXPR)
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 8eaa8dbb941..89665d88588 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.25 1998/10/21 16:21:24 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.26 1998/12/04 15:34:36 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,7 +39,6 @@
 #include "catalog/pg_type.h"
 
 
-
 static RewriteInfo *gatherRewriteMeta(Query *parsetree,
 				  Query *rule_action,
 				  Node *rule_qual,
@@ -55,17 +54,6 @@ static SubLink *modifyAggregMakeSublink(Expr *origexp, Query *parsetree);
 static void modifyAggregQual(Node **nodePtr, Query *parsetree);
 
 
-
-
-
-
-
-
-
-
-
-
-
 static Query *fireRIRrules(Query *parsetree);
 
 
@@ -293,6 +281,46 @@ rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
 			}
 			break;
 
+		case T_CaseExpr:
+			{
+				CaseExpr	*exp = (CaseExpr *)node;
+
+				if (rangeTableEntry_used(
+						(Node *)(exp->args),
+						rt_index,
+						sublevels_up))
+					return TRUE;
+
+				if (rangeTableEntry_used(
+						(Node *)(exp->defresult),
+						rt_index,
+						sublevels_up))
+					return TRUE;
+
+				return FALSE;
+			}
+			break;
+
+		case T_CaseWhen:
+			{
+				CaseWhen	*when = (CaseWhen *)node;
+
+				if (rangeTableEntry_used(
+						(Node *)(when->expr),
+						rt_index,
+						sublevels_up))
+					return TRUE;
+
+				if (rangeTableEntry_used(
+						(Node *)(when->result),
+						rt_index,
+						sublevels_up))
+					return TRUE;
+
+				return FALSE;
+			}
+			break;
+
 		case T_Query:
 			{
 				Query	*qry = (Query *)node;
@@ -1055,10 +1083,12 @@ modifyAggregMakeSublink(Expr *origexp, Query *parsetree)
 	Expr		*exp = copyObject(origexp);
 
 	if (nodeTag(nth(0, exp->args)) == T_Aggreg)
+	{
 		if (nodeTag(nth(1, exp->args)) == T_Aggreg)
 			elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported");
 		else
 			elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual");
+	}
 
 	aggreg = (Aggreg *)nth(1, exp->args);
 	target	= (Var *)(aggreg->target);
@@ -1189,6 +1219,33 @@ modifyAggregQual(Node **nodePtr, Query *parsetree)
 			}
 			break;
 
+		case T_CaseExpr:
+			{
+				/* We're calling recursively,
+				 * and this routine knows how to handle lists
+				 * so let it do the work to handle the WHEN clauses... */
+				modifyAggregQual(
+						(Node **)(&(((CaseExpr *)node)->args)),
+						parsetree);
+
+				modifyAggregQual(
+						(Node **)(&(((CaseExpr *)node)->defresult)),
+						parsetree);
+			}
+			break;
+
+		case T_CaseWhen:
+			{
+				modifyAggregQual(
+						(Node **)(&(((CaseWhen *)node)->expr)),
+						parsetree);
+
+				modifyAggregQual(
+						(Node **)(&(((CaseWhen *)node)->result)),
+						parsetree);
+			}
+			break;
+
 		case T_Iter:
 			{
 				Iter	*iter = (Iter *)node;
@@ -1827,6 +1884,10 @@ fireRIRonSubselect(Node *node)
 			}
 			break;
 
+		case T_CaseExpr:
+		case T_CaseWhen:
+			break;
+
 		case T_Query:
 			{
 				Query	*qry = (Query *)node;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5ddbfd142e0..526e847de99 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.31 1998/10/01 02:04:01 tgl Exp $
+ * $Id: nodes.h,v 1.32 1998/12/04 15:34:44 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -213,11 +213,13 @@ typedef enum NodeTag
 	T_SortClause,
 	T_GroupClause,
 	T_SubSelect,
-	T_JoinUsing
+	T_JoinUsing,
+	T_CaseExpr,
+	T_CaseWhen
 } NodeTag;
 
 /*
- * The first field of a node of any type is gauranteed to be the NodeTag.
+ * The first field of a node of any type is guaranteed to be the NodeTag.
  * Hence the type of any node can be gotten by casting it to Node. Declaring
  * a variable to be of Node * (instead of void *) can also facilitate
  * debugging.
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index afe6fe43f77..20fc0788bd4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.61 1998/10/22 13:52:24 momjian Exp $
+ * $Id: parsenodes.h,v 1.62 1998/12/04 15:34:44 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -713,6 +713,28 @@ typedef struct A_Const
 	TypeName   *typename;		/* typecast */
 } A_Const;
 
+/*
+ * CaseExpr - a CASE expression
+ */
+typedef struct CaseExpr
+{
+	NodeTag		type;
+	Oid			casetype;
+	Node	   *arg;			/* implicit equality comparison argument */
+	List	   *args;			/* the arguments (list of WHEN clauses) */
+	Node	   *defresult;		/* the default result (ELSE clause) */
+} CaseExpr;
+
+/*
+ * CaseWhen - an argument to a CASE expression
+ */
+typedef struct CaseWhen
+{
+	NodeTag		type;
+	Node	   *expr;			/* comparison expression */
+	Node	   *result;			/* substitution result */
+} CaseWhen;
+
 /*
  * ColumnDef - column definition (used in various creates)
  */
@@ -777,7 +799,7 @@ typedef struct ResTarget
 } ResTarget;
 
 /*
- * ParamString - used in with clauses
+ * ParamString - used in WITH clauses
  */
 typedef struct ParamString
 {
@@ -797,7 +819,7 @@ typedef struct RelExpr
 } RelExpr;
 
 /*
- * SortGroupBy - for order by clause
+ * SortGroupBy - for ORDER BY clause
  */
 typedef struct SortGroupBy
 {
@@ -807,7 +829,7 @@ typedef struct SortGroupBy
 } SortGroupBy;
 
 /*
- * JoinUsing - for join using clause
+ * JoinUsing - for JOIN USING clause
  */
 typedef struct JoinUsing
 {
@@ -818,7 +840,7 @@ typedef struct JoinUsing
 }			JoinUsing;
 
 /*
- * RangeVar - range variable, used in from clauses
+ * RangeVar - range variable, used in FROM clauses
  */
 typedef struct RangeVar
 {
@@ -828,7 +850,7 @@ typedef struct RangeVar
 } RangeVar;
 
 /*
- * IndexElem - index parameters (used in create index)
+ * IndexElem - index parameters (used in CREATE INDEX)
  */
 typedef struct IndexElem
 {
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 08f4a84f10b..3f93920af83 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.13 1998/09/01 04:36:53 momjian Exp $
+ * $Id: clauses.h,v 1.14 1998/12/04 15:34:49 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,8 @@ extern Expr *get_notclausearg(Expr *notclause);
 extern bool and_clause(Node *clause);
 extern Expr *make_andclause(List *andclauses);
 
+extern bool case_clause(Node *clause);
+
 extern List *pull_constant_clauses(List *quals, List **constantQual);
 extern void clause_get_relids_vars(Node *clause, List **relids, List **vars);
 extern int	NumRelids(Node *clause);
-- 
GitLab