diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 1a9743fb5f38f9fc9398e7818e215eaabb7e9b29..14c3745e5e91d282bd015f64b723f14c5c4caac7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.256 2003/11/29 19:51:42 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.257 2003/12/28 21:57:36 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1344,10 +1344,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
 	int16	   *attNos;
 
 	/*
-	 * Convert condition to a normal boolean expression tree.
+	 * Convert condition to an expression tree.
 	 */
 	expr = stringToNode(ccbin);
-	expr = (Node *) make_ands_explicit((List *) expr);
 
 	/*
 	 * deparse it
@@ -1651,14 +1650,6 @@ AddRelationRawConstraints(Relation rel,
 					(errcode(ERRCODE_GROUPING_ERROR),
 					 errmsg("cannot use aggregate function in check constraint")));
 
-		/*
-		 * Constraints are evaluated with execQual, which expects an
-		 * implicit-AND list, so convert expression to implicit-AND form.
-		 * (We could go so far as to convert to CNF, but that's probably
-		 * overkill...)
-		 */
-		expr = (Node *) make_ands_implicit((Expr *) expr);
-
 		/*
 		 * OK, store it.
 		 */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a05d74849e5686f6de1fb4d1da54a6d22cdcc9f0..59effe8e3213e80e996e956111e754a85211ae78 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.223 2003/11/29 19:51:43 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.224 2003/12/28 21:57:36 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -397,13 +397,14 @@ UpdateIndexRelation(Oid indexoid,
 		exprsDatum = (Datum) 0;
 
 	/*
-	 * Convert the index predicate (if any) to a text datum
+	 * Convert the index predicate (if any) to a text datum.  Note we
+	 * convert implicit-AND format to normal explicit-AND for storage.
 	 */
 	if (indexInfo->ii_Predicate != NIL)
 	{
 		char	   *predString;
 
-		predString = nodeToString(indexInfo->ii_Predicate);
+		predString = nodeToString(make_ands_explicit(indexInfo->ii_Predicate));
 		predDatum = DirectFunctionCall1(textin,
 										CStringGetDatum(predString));
 		pfree(predString);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 863d74f59a30a658c91e5c34f2233d204e89e1cd..507bcf50d5fb3d85600e5c50bb66b728d38e57cd 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.116 2003/11/29 19:51:47 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.117 2003/12/28 21:57:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,7 @@
 
 
 /* non-export function prototypes */
-static void CheckPredicate(List *predList);
+static void CheckPredicate(Expr *predicate);
 static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
 				  List *attList,
 				  Oid relId,
@@ -80,7 +80,6 @@ DefineIndex(RangeVar *heapRelation,
 	Form_pg_am	accessMethodForm;
 	IndexInfo  *indexInfo;
 	int			numberOfAttributes;
-	List	   *cnfPred = NIL;
 
 	/*
 	 * count attributes in index
@@ -172,16 +171,10 @@ DefineIndex(RangeVar *heapRelation,
 	}
 
 	/*
-	 * Convert the partial-index predicate from parsetree form to an
-	 * implicit-AND qual expression, for easier evaluation at runtime.
-	 * While we are at it, we reduce it to a canonical (CNF or DNF) form
-	 * to simplify the task of proving implications.
+	 * Validate predicate, if given
 	 */
 	if (predicate)
-	{
-		cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
-		CheckPredicate(cnfPred);
-	}
+		CheckPredicate(predicate);
 
 	/*
 	 * Check that all of the attributes in a primary key are marked as not
@@ -237,13 +230,13 @@ DefineIndex(RangeVar *heapRelation,
 
 	/*
 	 * Prepare arguments for index_create, primarily an IndexInfo
-	 * structure
+	 * structure.  Note that ii_Predicate must be in implicit-AND format.
 	 */
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_NumIndexAttrs = numberOfAttributes;
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_Predicate = cnfPred;
+	indexInfo->ii_Predicate = make_ands_implicit(predicate);
 	indexInfo->ii_PredicateState = NIL;
 	indexInfo->ii_Unique = unique;
 
@@ -268,7 +261,7 @@ DefineIndex(RangeVar *heapRelation,
 
 /*
  * CheckPredicate
- *		Checks that the given list of partial-index predicates is valid.
+ *		Checks that the given partial-index predicate is valid.
  *
  * This used to also constrain the form of the predicate to forms that
  * indxpath.c could do something with.	However, that seems overly
@@ -278,18 +271,18 @@ DefineIndex(RangeVar *heapRelation,
  * (except ones requiring a plan), and let indxpath.c fend for itself.
  */
 static void
-CheckPredicate(List *predList)
+CheckPredicate(Expr *predicate)
 {
 	/*
 	 * We don't currently support generation of an actual query plan for a
 	 * predicate, only simple scalar expressions; hence these
 	 * restrictions.
 	 */
-	if (contain_subplans((Node *) predList))
+	if (contain_subplans((Node *) predicate))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot use subquery in index predicate")));
-	if (contain_agg_clause((Node *) predList))
+	if (contain_agg_clause((Node *) predicate))
 		ereport(ERROR,
 				(errcode(ERRCODE_GROUPING_ERROR),
 				 errmsg("cannot use aggregate in index predicate")));
@@ -298,7 +291,7 @@ CheckPredicate(List *predList)
 	 * A predicate using mutable functions is probably wrong, for the same
 	 * reasons that we don't allow an index expression to use one.
 	 */
-	if (contain_mutable_functions((Node *) predList))
+	if (contain_mutable_functions((Node *) predicate))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 		errmsg("functions in index predicate must be marked IMMUTABLE")));
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index e53bb1f7040203a3db07bcf7081bf099e53117a8..f84b4dd690e290716321d240dec98c993b858cfe 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.223 2003/12/01 22:07:58 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.224 2003/12/28 21:57:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@
 #include "executor/execdebug.h"
 #include "executor/execdefs.h"
 #include "miscadmin.h"
+#include "optimizer/clauses.h"
 #include "optimizer/var.h"
 #include "parser/parsetree.h"
 #include "utils/acl.h"
@@ -1658,7 +1659,8 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 			(List **) palloc(ncheck * sizeof(List *));
 		for (i = 0; i < ncheck; i++)
 		{
-			qual = (List *) stringToNode(check[i].ccbin);
+			/* ExecQual wants implicit-AND form */
+			qual = make_ands_implicit(stringToNode(check[i].ccbin));
 			resultRelInfo->ri_ConstraintExprs[i] = (List *)
 				ExecPrepareExpr((Expr *) qual, estate);
 		}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index bdc146a028ecacdc707ab9a23162dd9d343264bb..bf8a28d12546aeba201cd8b72de6169e5fb56c0a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.162 2003/11/29 19:51:50 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.163 2003/12/28 21:57:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -377,23 +377,13 @@ preprocess_expression(Query *parse, Node *expr, int kind)
 		expr = flatten_join_alias_vars(parse, expr);
 
 	/*
-	 * Simplify constant expressions.
-	 *
-	 * Note that at this point quals have not yet been converted to
-	 * implicit-AND form, so we can apply eval_const_expressions directly.
-	 */
-	expr = eval_const_expressions(expr);
-
-	/*
-	 * If it's a qual or havingQual, canonicalize it, and convert it to
-	 * implicit-AND format.
-	 *
-	 * XXX Is there any value in re-applying eval_const_expressions after
-	 * canonicalize_qual?
+	 * If it's a qual or havingQual, canonicalize it.  It seems most useful
+	 * to do this before applying eval_const_expressions, since the latter
+	 * can optimize flattened AND/ORs better than unflattened ones.
 	 */
 	if (kind == EXPRKIND_QUAL)
 	{
-		expr = (Node *) canonicalize_qual((Expr *) expr, true);
+		expr = (Node *) canonicalize_qual((Expr *) expr);
 
 #ifdef OPTIMIZER_DEBUG
 		printf("After canonicalize_qual()\n");
@@ -401,6 +391,19 @@ preprocess_expression(Query *parse, Node *expr, int kind)
 #endif
 	}
 
+	/*
+	 * Simplify constant expressions.
+	 */
+	expr = eval_const_expressions(expr);
+
+	/*
+	 * If it's a qual or havingQual, convert it to implicit-AND format.
+	 * (We don't want to do this before eval_const_expressions, since the
+	 * latter would be unable to simplify a top-level AND correctly.)
+	 */
+	if (kind == EXPRKIND_QUAL)
+		expr = (Node *) make_ands_implicit((Expr *) expr);
+
 	/* Expand SubLinks to SubPlans */
 	if (parse->hasSubLinks)
 		expr = SS_process_sublinks(expr, (kind == EXPRKIND_QUAL));
diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c
index 1bff3509e26fe11ab7e054295469dd157674201e..186de4c40ac20c1e19395e47447474d4800af341 100644
--- a/src/backend/optimizer/prep/prepqual.c
+++ b/src/backend/optimizer/prep/prepqual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.39 2003/11/29 19:51:51 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.40 2003/12/28 21:57:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 #include "optimizer/prep.h"
 #include "utils/lsyscache.h"
 
-static Expr *flatten_andors(Expr *qual);
+static Node *flatten_andors_mutator(Node *node, void *context);
 static void flatten_andors_and_walker(FastList *out_list, List *andlist);
 static void flatten_andors_or_walker(FastList *out_list, List *orlist);
 static List *pull_ands(List *andlist);
@@ -61,8 +61,7 @@ static void count_bool_nodes(Expr *qual, double *nodes,
  *
  *		canonicalize_qual() does "smart" conversion to either CNF or DNF, per
  *		the above considerations, while cnfify() and dnfify() simply perform
- *		the demanded transformation.  The latter two may become dead code
- *		eventually.
+ *		the demanded transformation.  The latter two are presently dead code.
  *****************************************************************************/
 
 
@@ -72,11 +71,6 @@ static void count_bool_nodes(Expr *qual, double *nodes,
  *
  * Returns the modified qualification.
  *
- * If 'removeAndFlag' is true then it removes explicit AND at the top level,
- * producing a list of implicitly-ANDed conditions.  Otherwise, a regular
- * boolean expression is returned.	Since most callers pass 'true', we
- * prefer to declare the result as List *, not Expr *.
- *
  * XXX This code could be much smarter, at the cost of also being slower,
  * if we tried to compute selectivities and/or see whether there are
  * actually indexes to support an indexscan implementation of a DNF qual.
@@ -85,8 +79,8 @@ static void count_bool_nodes(Expr *qual, double *nodes,
  * implement.  For now, though, we just try to avoid doing anything
  * quite as stupid as unconditionally converting to CNF was...
  */
-List *
-canonicalize_qual(Expr *qual, bool removeAndFlag)
+Expr *
+canonicalize_qual(Expr *qual)
 {
 	Expr	   *newqual;
 	double		nodes,
@@ -95,23 +89,23 @@ canonicalize_qual(Expr *qual, bool removeAndFlag)
 	bool		cnfok,
 				dnfok;
 
+	/* Quick exit for empty qual */
 	if (qual == NULL)
-		return NIL;
+		return NULL;
 
 	/*
 	 * Flatten AND and OR groups throughout the tree. This improvement is
 	 * always worthwhile, so do it unconditionally.
 	 */
-	qual = flatten_andors(qual);
+	newqual = (Expr *) flatten_andors((Node *) qual);
 
 	/*
 	 * Push down NOTs.	We do this only in the top-level boolean
 	 * expression, without examining arguments of operators/functions.
-	 * Even so, it might not be a win if we are unable to find negators
-	 * for all the operators involved; perhaps we should compare before-
-	 * and-after tree sizes?
+	 * The main reason for doing this is to expose as much top-level AND/OR
+	 * structure as we can, so there's no point in descending further.
 	 */
-	newqual = find_nots(qual);
+	newqual = find_nots(newqual);
 
 	/*
 	 * Choose whether to convert to CNF, or DNF, or leave well enough
@@ -175,13 +169,10 @@ canonicalize_qual(Expr *qual, bool removeAndFlag)
 		newqual = qual_cleanup(find_ands(newqual));
 	}
 
-	/* Convert to implicit-AND list if requested */
-	if (removeAndFlag)
-		newqual = (Expr *) make_ands_implicit(newqual);
-
-	return (List *) newqual;
+	return newqual;
 }
 
+#ifdef NOT_USED
 /*
  * cnfify
  *	  Convert a qualification to conjunctive normal form by applying
@@ -223,6 +214,7 @@ cnfify(Expr *qual, bool removeAndFlag)
 
 	return (List *) newqual;
 }
+#endif
 
 #ifdef NOT_USED
 /*
@@ -281,50 +273,46 @@ dnfify(Expr *qual)
 
 /*--------------------
  * flatten_andors
- *	  Given a qualification, simplify nested AND/OR clauses into flat
- *	  AND/OR clauses with more arguments.
+ *	  Given an expression tree, simplify nested AND/OR clauses into flat
+ *	  AND/OR clauses with more arguments.  The entire tree is processed.
  *
- * Returns the rebuilt expr (note original list structure is not touched).
+ * Returns the rebuilt expr (note original structure is not touched).
  *--------------------
  */
-static Expr *
-flatten_andors(Expr *qual)
+Node *
+flatten_andors(Node *node)
 {
-	if (qual == NULL)
-		return NULL;
+	return flatten_andors_mutator(node, NULL);
+}
 
-	if (and_clause((Node *) qual))
+static Node *
+flatten_andors_mutator(Node *node, void *context)
+{
+	if (node == NULL)
+		return NULL;
+	if (IsA(node, BoolExpr))
 	{
-		FastList	out_list;
+		BoolExpr   *bexpr = (BoolExpr *) node;
 
-		FastListInit(&out_list);
-		flatten_andors_and_walker(&out_list, ((BoolExpr *) qual)->args);
-		return make_andclause(FastListValue(&out_list));
-	}
-	else if (or_clause((Node *) qual))
-	{
-		FastList	out_list;
+		if (bexpr->boolop == AND_EXPR)
+		{
+			FastList	out_list;
 
-		FastListInit(&out_list);
-		flatten_andors_or_walker(&out_list, ((BoolExpr *) qual)->args);
-		return make_orclause(FastListValue(&out_list));
-	}
-	else if (not_clause((Node *) qual))
-		return make_notclause(flatten_andors(get_notclausearg(qual)));
-	else if (is_opclause(qual))
-	{
-		OpExpr	   *opexpr = (OpExpr *) qual;
-		Expr	   *left = (Expr *) get_leftop(qual);
-		Expr	   *right = (Expr *) get_rightop(qual);
-
-		return make_opclause(opexpr->opno,
-							 opexpr->opresulttype,
-							 opexpr->opretset,
-							 flatten_andors(left),
-							 flatten_andors(right));
+			FastListInit(&out_list);
+			flatten_andors_and_walker(&out_list, bexpr->args);
+			return (Node *) make_andclause(FastListValue(&out_list));
+		}
+		if (bexpr->boolop == OR_EXPR)
+		{
+			FastList	out_list;
+
+			FastListInit(&out_list);
+			flatten_andors_or_walker(&out_list, bexpr->args);
+			return (Node *) make_orclause(FastListValue(&out_list));
+		}
+		/* else it's a NOT clause, fall through */
 	}
-	else
-		return qual;
+	return expression_tree_mutator(node, flatten_andors_mutator, context);
 }
 
 static void
@@ -334,9 +322,9 @@ flatten_andors_and_walker(FastList *out_list, List *andlist)
 
 	foreach(arg, andlist)
 	{
-		Expr	   *subexpr = (Expr *) lfirst(arg);
+		Node	   *subexpr = (Node *) lfirst(arg);
 
-		if (and_clause((Node *) subexpr))
+		if (and_clause(subexpr))
 			flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args);
 		else
 			FastAppend(out_list, flatten_andors(subexpr));
@@ -350,9 +338,9 @@ flatten_andors_or_walker(FastList *out_list, List *orlist)
 
 	foreach(arg, orlist)
 	{
-		Expr	   *subexpr = (Expr *) lfirst(arg);
+		Node	   *subexpr = (Node *) lfirst(arg);
 
-		if (or_clause((Node *) subexpr))
+		if (or_clause(subexpr))
 			flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args);
 		else
 			FastAppend(out_list, flatten_andors(subexpr));
@@ -383,9 +371,9 @@ pull_ands_walker(FastList *out_list, List *andlist)
 
 	foreach(arg, andlist)
 	{
-		Expr	   *subexpr = (Expr *) lfirst(arg);
+		Node	   *subexpr = (Node *) lfirst(arg);
 
-		if (and_clause((Node *) subexpr))
+		if (and_clause(subexpr))
 			pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args);
 		else
 			FastAppend(out_list, subexpr);
@@ -416,9 +404,9 @@ pull_ors_walker(FastList *out_list, List *orlist)
 
 	foreach(arg, orlist)
 	{
-		Expr	   *subexpr = (Expr *) lfirst(arg);
+		Node	   *subexpr = (Node *) lfirst(arg);
 
-		if (or_clause((Node *) subexpr))
+		if (or_clause(subexpr))
 			pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args);
 		else
 			FastAppend(out_list, subexpr);
@@ -427,9 +415,10 @@ pull_ors_walker(FastList *out_list, List *orlist)
 
 /*
  * find_nots
- *	  Traverse the qualification, looking for 'NOT's to take care of.
- *	  For 'NOT' clauses, apply push_not() to try to push down the 'NOT'.
- *	  For all other clause types, simply recurse.
+ *	  Traverse the qualification, looking for NOTs to take care of.
+ *	  For NOT clauses, apply push_nots() to try to push down the NOT.
+ *	  For AND and OR clause types, simply recurse.  Otherwise stop
+ *	  recursing (we do not worry about structure below the top AND/OR tree).
  *
  * Returns the modified qualification.	AND/OR flatness is preserved.
  */
@@ -439,21 +428,6 @@ find_nots(Expr *qual)
 	if (qual == NULL)
 		return NULL;
 
-#ifdef NOT_USED
-	/* recursing into operator expressions is probably not worth it. */
-	if (is_opclause(qual))
-	{
-		OpExpr	   *opexpr = (OpExpr *) qual;
-		Expr	   *left = (Expr *) get_leftop(qual);
-		Expr	   *right = (Expr *) get_rightop(qual);
-
-		return make_opclause(opexpr->opno,
-							 opexpr->opresulttype,
-							 opexpr->opretset,
-							 find_nots(left),
-							 find_nots(right));
-	}
-#endif
 	if (and_clause((Node *) qual))
 	{
 		FastList	t_list;
@@ -482,7 +456,7 @@ find_nots(Expr *qual)
 
 /*
  * push_nots
- *	  Push down a 'NOT' as far as possible.
+ *	  Push down a NOT as far as possible.
  *
  * Input is an expression to be negated (e.g., the argument of a NOT clause).
  * Returns a new qual equivalent to the negation of the given qual.
@@ -496,7 +470,7 @@ push_nots(Expr *qual)
 
 	/*
 	 * Negate an operator clause if possible: ("NOT" (< A B)) => (> A B)
-	 * Otherwise, retain the clause as it is (the 'not' can't be pushed
+	 * Otherwise, retain the clause as it is (the NOT can't be pushed
 	 * down any farther).
 	 */
 	if (is_opclause(qual))
@@ -543,16 +517,15 @@ push_nots(Expr *qual)
 	else if (not_clause((Node *) qual))
 	{
 		/*
-		 * Another 'not' cancels this 'not', so eliminate the 'not' and
-		 * stop negating this branch.  But search the subexpression for
-		 * more 'not's to simplify.
+		 * Another NOT cancels this NOT, so eliminate the NOT and
+		 * stop negating this branch.
 		 */
-		return find_nots(get_notclausearg(qual));
+		return get_notclausearg(qual);
 	}
 	else
 	{
 		/*
-		 * We don't know how to negate anything else, place a 'not' at
+		 * We don't know how to negate anything else, place a NOT at
 		 * this level.
 		 */
 		return make_notclause(qual);
@@ -561,12 +534,13 @@ push_nots(Expr *qual)
 
 /*
  * find_ors
- *	  Given a qualification tree with the 'not's pushed down, convert it
+ *	  Given a qualification tree with the NOTs pushed down, convert it
  *	  to a tree in CNF by repeatedly applying the rule:
  *				("OR" A ("AND" B C))  => ("AND" ("OR" A B) ("OR" A C))
  *
  *	  Note that 'or' clauses will always be turned into 'and' clauses
- *	  if they contain any 'and' subclauses.
+ *	  if they contain any 'and' subclauses.  Also, we do not descend
+ *	  below the top-level AND/OR structure.
  *
  * Returns the modified qualification.	AND/OR flatness is preserved.
  */
@@ -576,7 +550,6 @@ find_ors(Expr *qual)
 	if (qual == NULL)
 		return NULL;
 
-	/* We used to recurse into opclauses here, but I see no reason to... */
 	if (and_clause((Node *) qual))
 	{
 		List	   *andlist = NIL;
@@ -595,8 +568,6 @@ find_ors(Expr *qual)
 			orlist = lappend(orlist, find_ors(lfirst(temp)));
 		return or_normalize(pull_ors(orlist));
 	}
-	else if (not_clause((Node *) qual))
-		return make_notclause(find_ors(get_notclausearg(qual)));
 	else
 		return qual;
 }
@@ -688,12 +659,13 @@ or_normalize(List *orlist)
 
 /*
  * find_ands
- *	  Given a qualification tree with the 'not's pushed down, convert it
+ *	  Given a qualification tree with the NOTs pushed down, convert it
  *	  to a tree in DNF by repeatedly applying the rule:
  *				("AND" A ("OR" B C))  => ("OR" ("AND" A B) ("AND" A C))
  *
  *	  Note that 'and' clauses will always be turned into 'or' clauses
- *	  if they contain any 'or' subclauses.
+ *	  if they contain any 'or' subclauses.  Also, we do not descend
+ *	  below the top-level AND/OR structure.
  *
  * Returns the modified qualification.	AND/OR flatness is preserved.
  */
@@ -703,7 +675,6 @@ find_ands(Expr *qual)
 	if (qual == NULL)
 		return NULL;
 
-	/* We used to recurse into opclauses here, but I see no reason to... */
 	if (or_clause((Node *) qual))
 	{
 		List	   *orlist = NIL;
@@ -722,8 +693,6 @@ find_ands(Expr *qual)
 			andlist = lappend(andlist, find_ands(lfirst(temp)));
 		return and_normalize(pull_ands(andlist));
 	}
-	else if (not_clause((Node *) qual))
-		return make_notclause(find_ands(get_notclausearg(qual)));
 	else
 		return qual;
 }
@@ -860,8 +829,6 @@ qual_cleanup(Expr *qual)
 		else
 			return lfirst(orlist);
 	}
-	else if (not_clause((Node *) qual))
-		return make_notclause(qual_cleanup(get_notclausearg(qual)));
 	else
 		return qual;
 }
@@ -889,14 +856,14 @@ remove_duplicates(List *list)
 /*
  * count_bool_nodes
  *		Support for heuristics in canonicalize_qual(): count the
- *		number of nodes that are inputs to the top level AND/OR/NOT
+ *		number of nodes that are inputs to the top level AND/OR
  *		part of a qual tree, and estimate how many nodes will appear
  *		in the CNF'ified or DNF'ified equivalent of the expression.
  *
- * This is just an approximate calculation; it doesn't deal with NOTs
- * very well, and of course it cannot detect possible simplifications
- * from eliminating duplicate subclauses.  The idea is just to cheaply
- * determine whether CNF will be markedly worse than DNF or vice versa.
+ * This is just an approximate calculation; it cannot detect possible
+ * simplifications from eliminating duplicate subclauses.  The idea is just to
+ * cheaply determine whether CNF will be markedly worse than DNF or vice
+ * versa.
  *
  * The counts/estimates are represented as doubles to avoid risk of overflow.
  */
@@ -953,11 +920,6 @@ count_bool_nodes(Expr *qual,
 		if (*cnfnodes < *dnfnodes)
 			*cnfnodes = *dnfnodes;
 	}
-	else if (not_clause((Node *) qual))
-	{
-		count_bool_nodes(get_notclausearg(qual),
-						 nodes, cnfnodes, dnfnodes);
-	}
 	else if (contain_subplans((Node *) qual))
 	{
 		/*
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 697799a494e303cbdea332379ca66b2855c6263b..d6f0bb3ad2eff317545f20465432b4180a2d8410 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.156 2003/12/09 01:56:20 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.157 2003/12/28 21:57:37 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -261,8 +261,8 @@ make_and_qual(Node *qual1, Node *qual2)
 }
 
 /*
- * Sometimes (such as in the result of canonicalize_qual or the input of
- * ExecQual), we use lists of expression nodes with implicit AND semantics.
+ * Sometimes (such as in the input of ExecQual), we use lists of expression
+ * nodes with implicit AND semantics.
  *
  * These functions convert between an AND-semantics expression list and the
  * ordinary representation of a boolean expression.
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ca3c3720519099f5743b69cc13f209db84e2fefa..f8937c7ea649cb6d11661fb1d95ddca20d27e067 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.160 2003/11/29 19:51:59 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.161 2003/12/28 21:57:37 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -810,13 +810,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
 			node = (Node *) stringToNode(predString);
 			pfree(predString);
 
-			/*
-			 * If top level is a List, assume it is an implicit-AND
-			 * structure, and convert to explicit AND.	This is needed for
-			 * partial index predicates.
-			 */
-			if (node && IsA(node, List))
-				node = (Node *) make_ands_explicit((List *) node);
 			/* Deparse */
 			str = deparse_expression_pretty(node, context, false, false,
 											prettyFlags, 0);
@@ -1060,14 +1053,6 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags)
 				conbin = DatumGetCString(DirectFunctionCall1(textout, val));
 				expr = stringToNode(conbin);
 
-				/*
-				 * If top level is a List, assume it is an implicit-AND
-				 * structure, and convert to explicit AND.	This is needed
-				 * for partial index predicates.
-				 */
-				if (expr && IsA(expr, List))
-					expr = (Node *) make_ands_explicit((List *) expr);
-
 				/* Set up deparsing context for Var nodes in constraint */
 				if (conForm->conrelid != InvalidOid)
 				{
@@ -1212,14 +1197,6 @@ pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags)
 	/* Convert expression to node tree */
 	node = (Node *) stringToNode(exprstr);
 
-	/*
-	 * If top level is a List, assume it is an implicit-AND structure, and
-	 * convert to explicit AND.  This is needed for partial index
-	 * predicates.
-	 */
-	if (node && IsA(node, List))
-		node = (Node *) make_ands_explicit((List *) node);
-
 	/* Deparse */
 	context = deparse_context_for(relname, relid);
 	str = deparse_expression_pretty(node, context, false, false,
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 9b2e86224a5473191c2b47bf6653ec3bafafafcb..0a37df1d9a797df768970b8f758e1c89adb134f2 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.150 2003/12/07 04:14:10 joe Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.151 2003/12/28 21:57:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3902,12 +3902,12 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 	 * If the index is partial, AND the index predicate with the
 	 * explicitly given indexquals to produce a more accurate idea of the
 	 * index restriction.  This may produce redundant clauses, which we
-	 * hope that cnfify and clauselist_selectivity will deal with
+	 * hope that canonicalize_qual and clauselist_selectivity will deal with
 	 * intelligently.
 	 *
 	 * Note that index->indpred and indexQuals are both in implicit-AND form
 	 * to start with, which we have to make explicit to hand to
-	 * canonicalize_qual, and then we get back implicit-AND form again.
+	 * canonicalize_qual, and then we convert back to implicit-AND form.
 	 */
 	if (index->indpred != NIL)
 	{
@@ -3915,7 +3915,8 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 
 		andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
 											  indexQuals));
-		selectivityQuals = canonicalize_qual(andedQuals, true);
+		andedQuals = canonicalize_qual(andedQuals);
+		selectivityQuals = make_ands_implicit(andedQuals);
 	}
 
 	/* Estimate the fraction of main-table tuples that will be visited */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 679a73644e446a7fb16ae1f36886b0541113ab9d..797d96ca8ecff62d46519aed82560059a7f8aca5 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.193 2003/11/29 19:52:00 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.194 2003/12/28 21:57:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,7 @@
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
+#include "optimizer/prep.h"
 #include "storage/smgr.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
@@ -2770,11 +2771,13 @@ RelationGetIndexExpressions(Relation relation)
 	pfree(exprsString);
 
 	/*
-	 * Run the expressions through eval_const_expressions.	This is not
-	 * just an optimization, but is necessary, because the planner will be
-	 * comparing them to const-folded qual clauses, and may fail to detect
-	 * valid matches without this.
+	 * Run the expressions through flatten_andors and eval_const_expressions.
+	 * This is not just an optimization, but is necessary, because the planner
+	 * will be comparing them to similarly-processed qual clauses, and may
+	 * fail to detect valid matches without this.
 	 */
+	result = (List *) flatten_andors((Node *) result);
+
 	result = (List *) eval_const_expressions((Node *) result);
 
 	/* May as well fix opfuncids too */
@@ -2791,7 +2794,8 @@ RelationGetIndexExpressions(Relation relation)
 /*
  * RelationGetIndexPredicate -- get the index predicate for an index
  *
- * We cache the result of transforming pg_index.indpred into a node tree.
+ * We cache the result of transforming pg_index.indpred into an implicit-AND
+ * node tree (suitable for ExecQual).
  * If the rel is not an index or has no predicate, we return NIL.
  * Otherwise, the returned tree is copied into the caller's memory context.
  * (We don't want to return a pointer to the relcache copy, since it could
@@ -2835,13 +2839,18 @@ RelationGetIndexPredicate(Relation relation)
 	pfree(predString);
 
 	/*
-	 * Run the expression through eval_const_expressions.  This is not
-	 * just an optimization, but is necessary, because the planner will be
-	 * comparing it to const-folded qual clauses, and may fail to detect
-	 * valid matches without this.
+	 * Run the expression through canonicalize_qual and eval_const_expressions.
+	 * This is not just an optimization, but is necessary, because the planner
+	 * will be comparing it to similarly-processed qual clauses, and may fail
+	 * to detect valid matches without this.
 	 */
+	result = (List *) canonicalize_qual((Expr *) result);
+
 	result = (List *) eval_const_expressions((Node *) result);
 
+	/* Also convert to implicit-AND format */
+	result = make_ands_implicit((Expr *) result);
+
 	/* May as well fix opfuncids too */
 	fix_opfuncids((Node *) result);
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0af05e01d6efc6c41f472b6f32e8491cf4a644fa..b5f0da8d0dc767760d0de249395d0d7b0347997b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.213 2003/12/03 18:53:52 joe Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.214 2003/12/28 21:57:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200312031
+#define CATALOG_VERSION_NO	200312281
 
 #endif
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 62e77afcd2e21f2dc325ea873345c0b70c314a09..6b205b20da5a2de1dcd05701e8a8be1f0a4eda9d 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.86 2003/11/29 22:41:06 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.87 2003/12/28 21:57:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -245,7 +245,8 @@ typedef struct RelOptInfo
  *		zero entry, rather than looking at ncolumns.
  *
  *		The indexprs and indpred expressions have been run through
- *		eval_const_expressions() for ease of matching to WHERE clauses.
+ *		prepqual.c and eval_const_expressions() for ease of matching to
+ *		WHERE clauses.  indpred is in implicit-AND form.
  */
 
 typedef struct IndexOptInfo
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index 060b25b314c4135de358b1639373fadc80249709..6255c3623075ecc26bcc4dab124de774bdbdeb25 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.42 2003/11/29 22:41:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/prep.h,v 1.43 2003/12/28 21:57:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,8 +36,8 @@ extern Relids get_relids_for_join(Query *parse, int joinrelid);
 /*
  * prototypes for prepqual.c
  */
-extern List *canonicalize_qual(Expr *qual, bool removeAndFlag);
-extern List *cnfify(Expr *qual, bool removeAndFlag);
+extern Expr *canonicalize_qual(Expr *qual);
+extern Node *flatten_andors(Node *node);
 
 /*
  * prototypes for preptlist.c