diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 9c9e8aeee7f0b30104dc9c9819ba8241be590f8f..edebfcbebe3799b4446bfb1865952362da709aec 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.49 2000/01/17 23:57:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.50 2000/01/19 23:54:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,7 +169,7 @@ gistbuild(Relation heap,
 #ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) oldPred, econtext) == true)
+			if (ExecQual((List *) oldPred, econtext, false))
 			{
 				ni++;
 				continue;
@@ -186,7 +186,7 @@ gistbuild(Relation heap,
 #ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) pred, econtext) == false)
+			if (! ExecQual((List *) pred, econtext, false))
 				continue;
 #endif	 /* OMIT_PARTIAL_INDEX */
 		}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index fd3e3f94d50edbe36c328673f29282db206805c1..1d88bfc8bc49eaf65032cc7a40c148915d9984d0 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.33 1999/12/10 03:55:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.34 2000/01/19 23:54:47 tgl Exp $
  *
  * NOTES
  *	  This file contains only the public interface routines.
@@ -131,7 +131,7 @@ hashbuild(Relation heap,
 			/* SetSlotContents(slot, htup); */
 #ifndef OMIT_PARTIAL_INDEX
 			slot->val = htup;
-			if (ExecQual((List *) oldPred, econtext) == true)
+			if (ExecQual((List *) oldPred, econtext, false))
 			{
 				nitups++;
 				continue;
@@ -148,7 +148,7 @@ hashbuild(Relation heap,
 #ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) pred, econtext) == false)
+			if (! ExecQual((List *) pred, econtext, false))
 				continue;
 #endif	 /* OMIT_PARTIAL_INDEX */
 		}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 458b427c70d17f7a9ee811449f0b86035235d660..1be5c08f973aeb7f8726013079e1265b260258d8 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -11,7 +11,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.50 1999/12/10 03:55:44 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.51 2000/01/19 23:54:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -167,7 +167,7 @@ btbuild(Relation heap,
 
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) oldPred, econtext) == true)
+			if (ExecQual((List *) oldPred, econtext, false))
 			{
 				nitups++;
 				continue;
@@ -184,7 +184,7 @@ btbuild(Relation heap,
 #ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) pred, econtext) == false)
+			if (! ExecQual((List *) pred, econtext, false))
 				continue;
 #endif	 /* OMIT_PARTIAL_INDEX */
 		}
diff --git a/src/backend/access/rtree/rtree.c b/src/backend/access/rtree/rtree.c
index 09930e1e340680637cd250cb73ce3544b5f4f82c..fd2a8e545a4e696d41b16faac3225ddf49f37f38 100644
--- a/src/backend/access/rtree/rtree.c
+++ b/src/backend/access/rtree/rtree.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.41 1999/12/10 03:55:45 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.42 2000/01/19 23:54:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -163,7 +163,7 @@ rtbuild(Relation heap,
 #ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) oldPred, econtext) == true)
+			if (ExecQual((List *) oldPred, econtext, false))
 			{
 				ni++;
 				continue;
@@ -180,7 +180,7 @@ rtbuild(Relation heap,
 #ifndef OMIT_PARTIAL_INDEX
 			/* SetSlotContents(slot, htup); */
 			slot->val = htup;
-			if (ExecQual((List *) pred, econtext) == false)
+			if (! ExecQual((List *) pred, econtext, false))
 				continue;
 #endif	 /* OMIT_PARTIAL_INDEX */
 		}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 84009d6282b636656099bd0396db8d3432a16fff..375aed9a00658aa1106cbdcb0e161b6656288150 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.102 2000/01/17 23:57:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.103 2000/01/19 23:54:51 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1590,7 +1590,7 @@ DefaultBuild(Relation heapRelation,
 		{
 			/* SetSlotContents(slot, heapTuple); */
 			slot->val = heapTuple;
-			if (ExecQual((List *) oldPred, econtext) == true)
+			if (ExecQual((List *) oldPred, econtext, false))
 			{
 				indtuples++;
 				continue;
@@ -1605,7 +1605,7 @@ DefaultBuild(Relation heapRelation,
 		{
 			/* SetSlotContents(slot, heapTuple); */
 			slot->val = heapTuple;
-			if (ExecQual((List *) predicate, econtext) == false)
+			if (! ExecQual((List *) predicate, econtext, false))
 				continue;
 		}
 #endif	 /* OMIT_PARTIAL_INDEX */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 328b2d644ebf5184757ec0887c3a679bcbd8ed35..60611398173310dcd60a48c519cf4372919081a6 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.96 2000/01/16 21:37:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.97 2000/01/19 23:54:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -886,7 +886,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
 						 */
 						slot->val = tuple;
 						/* SetSlotContents(slot, tuple); */
-						if (ExecQual((List *) indexPred[i], econtext) == false)
+						if (! ExecQual((List *) indexPred[i], econtext, false))
 							continue;
 #endif	 /* OMIT_PARTIAL_INDEX */
 					}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fa98950ac1ff237606af513fbd5d04a360c1e3c1..863c13b64ece802a7242adf0a96bc07c1459f8a6 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.105 2000/01/17 23:57:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.106 2000/01/19 23:54:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1487,7 +1487,6 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 	RangeTblEntry *rte = makeNode(RangeTblEntry);
 	List	   *rtlist;
 	List	   *qual;
-	bool		res;
 	int			i;
 
 	slot->val = tuple;
@@ -1526,9 +1525,12 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
 	{
 		qual = estate->es_result_relation_constraints[i];
 
-		res = ExecQual(qual, econtext);
-
-		if (!res)
+		/*
+		 * NOTE: SQL92 specifies that a NULL result from a constraint
+		 * expression is not to be treated as a failure.  Therefore,
+		 * tell ExecQual to return TRUE for NULL.
+		 */
+		if (! ExecQual(qual, econtext, true))
 			return check[i].ccname;
 	}
 
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index bd546597733dbb87350b8370b3c7bc87e192ee8c..9e9cbde83bfeffc84cc427e0ae2e322e2399d5a5 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.65 2000/01/10 17:14:34 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.66 2000/01/19 23:54:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1283,12 +1283,33 @@ ExecEvalExpr(Node *expression,
 /* ----------------------------------------------------------------
  *		ExecQual
  *
- *		Evaluates a conjunctive boolean expression and returns t
- *		iff none of the subexpressions are false (or null).
+ *		Evaluates a conjunctive boolean expression (qual list) and
+ *		returns true iff none of the subexpressions are false.
+ *		(We also return true if the list is empty.)
+ *
+ *	If some of the subexpressions yield NULL but none yield FALSE,
+ *	then the result of the conjunction is NULL (ie, unknown)
+ *	according to three-valued boolean logic.  In this case,
+ *	we return the value specified by the "resultForNull" parameter.
+ *
+ *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
+ *	since SQL specifies that tuples with null WHERE results do not
+ *	get selected.  On the other hand, callers evaluating constraint
+ *	conditions should pass resultForNull=TRUE, since SQL also specifies
+ *	that NULL constraint conditions are not failures.
+ *
+ *	NOTE: it would not be correct to use this routine to evaluate an
+ *	AND subclause of a boolean expression; for that purpose, a NULL
+ *	result must be returned as NULL so that it can be properly treated
+ *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
+ *	This routine is only used in contexts where a complete expression
+ *	is being evaluated and we know that NULL can be treated the same
+ *	as one boolean result or the other.
+ *
  * ----------------------------------------------------------------
  */
 bool
-ExecQual(List *qual, ExprContext *econtext)
+ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 {
 	List	   *qlist;
 
@@ -1302,18 +1323,18 @@ ExecQual(List *qual, ExprContext *econtext)
 	IncrProcessed();
 
 	/*
-	 * a "qual" is a list of clauses.  To evaluate the qual, we evaluate
-	 * each of the clauses in the list.  (For an empty list, we'll return
-	 * TRUE.)
+	 * Evaluate the qual conditions one at a time.  If we find a FALSE
+	 * result, we can stop evaluating and return FALSE --- the AND result
+	 * must be FALSE.  Also, if we find a NULL result when resultForNull
+	 * is FALSE, we can stop and return FALSE --- the AND result must be
+	 * FALSE or NULL in that case, and the caller doesn't care which.
 	 *
-	 * If any of the clauses return NULL, we treat this as FALSE.  This
-	 * is correct per the SQL spec: if any ANDed conditions are NULL, then
-	 * the AND result is either FALSE or NULL, and in either case the
-	 * WHERE condition fails.  NOTE: it would NOT be correct to use this
-	 * simplified logic in a sub-clause; ExecEvalAnd must do the full
-	 * three-state condition evaluation.  We can get away with simpler
-	 * logic here because we know how the result will be used.
+	 * If we get to the end of the list, we can return TRUE.  This will
+	 * happen when the AND result is indeed TRUE, or when the AND result
+	 * is NULL (one or more NULL subresult, with all the rest TRUE) and
+	 * the caller has specified resultForNull = TRUE.
 	 */
+
 	foreach(qlist, qual)
 	{
 		Node	   *clause = (Node *) lfirst(qlist);
@@ -1321,7 +1342,11 @@ ExecQual(List *qual, ExprContext *econtext)
 		bool		isNull;
 		bool		isDone;
 
-		/* if there is a null clause, consider the qualification to fail */
+		/*
+		 * If there is a null clause, consider the qualification to fail.
+		 * XXX is this still correct for constraints?  It probably shouldn't
+		 * happen at all ...
+		 */
 		if (clause == NULL)
 			return false;
 		/*
@@ -1329,10 +1354,17 @@ ExecQual(List *qual, ExprContext *econtext)
 		 * in the qualifications.
 		 */
 		expr_value = ExecEvalExpr(clause, econtext, &isNull, &isDone);
+
 		if (isNull)
-			return false;		/* treat NULL as FALSE */
-		if (DatumGetInt32(expr_value) == 0)
-			return false;
+		{
+			if (resultForNull == false)
+				return false;	/* treat NULL as FALSE */
+		}
+		else
+		{
+			if (DatumGetInt32(expr_value) == 0)
+				return false;	/* definitely FALSE */
+		}
 	}
 
 	return true;
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 4196fb7a8a4dbd94dd6d8ad6a63ef887d07d7b86..4803653e14cd508fba8a832f02f8d507e06f1e8b 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.9 1999/02/13 23:15:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.10 2000/01/19 23:54:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,9 +137,10 @@ ExecScan(Scan *node,
 
 		/*
 		 * add a check for non-nil qual here to avoid a function call to
-		 * ExecQual() when the qual is nil
+		 * ExecQual() when the qual is nil ... saves only a few cycles,
+		 * but they add up ...
 		 */
-		if (!qual || ExecQual(qual, econtext) == true)
+		if (!qual || ExecQual(qual, econtext, false))
 			break;
 	}
 
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index d168071e1fe9d17690bf82ccbbf6f7ffeaba98a7..4d5079ae6947eb7a869e1c2828fdd1ac028f5c47 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.51 1999/12/20 10:40:42 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.52 2000/01/19 23:54:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1137,7 +1137,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 	IndexInfo **indexInfoArray;
 	IndexInfo  *indexInfo;
 	Node	   *predicate;
-	bool		satisfied;
 	ExprContext *econtext;
 	InsertIndexResult result;
 	int			numberOfAttributes;
@@ -1178,8 +1177,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 			econtext->ecxt_scantuple = slot;
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			satisfied = ExecQual((List *) predicate, econtext);
-			if (satisfied == false)
+			if (! ExecQual((List *) predicate, econtext, false))
 				continue;
 		}
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 0a95c92347fb858ffd470245ef1203807364f51c..a40fd015af35ba43253c7d8d3ec64bcaaad7dc61 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -31,7 +31,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.60 1999/12/13 01:26:52 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.61 2000/01/19 23:54:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -580,7 +580,7 @@ ExecAgg(Agg *node)
 		 * Otherwise, return the tuple.
 		 */
 	}
-	while (! ExecQual(node->plan.qual, econtext));
+	while (! ExecQual(node->plan.qual, econtext, false));
 
 	return resultSlot;
 }
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 1995048e2dbc8cef2077efd6a14b27e8d0db0091..46307a2aa963788f3deeac83c3d49805934e9882 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -6,7 +6,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  *
- *	$Id: nodeHash.c,v 1.42 2000/01/09 00:26:18 tgl Exp $
+ *	$Id: nodeHash.c,v 1.43 2000/01/19 23:54:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -584,7 +584,6 @@ ExecScanHashBucket(HashJoinState *hjstate,
 	{
 		HeapTuple	heapTuple = &hashTuple->htup;
 		TupleTableSlot *inntuple;
-		bool		qualResult;
 
 		/* insert hashtable's tuple into exec slot so ExecQual sees it */
 		inntuple = ExecStoreTuple(heapTuple,	/* tuple to store */
@@ -593,9 +592,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 								  false);		/* do not pfree this tuple */
 		econtext->ecxt_innertuple = inntuple;
 
-		qualResult = ExecQual(hjclauses, econtext);
-
-		if (qualResult)
+		if (ExecQual(hjclauses, econtext, false))
 		{
 			hjstate->hj_CurTuple = hashTuple;
 			return heapTuple;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 9d5034307fe34a6c33cf9538bfe14bdee17dbacc..6f5d2cae1949777a8bcbba252284b6d3afd3ceb0 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.28 1999/12/16 22:19:44 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.29 2000/01/19 23:54:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,7 +54,6 @@ ExecHashJoin(HashJoin *node)
 	ExprContext *econtext;
 	HashJoinTable hashtable;
 	HeapTuple	curtuple;
-	bool		qualResult;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
 	Var		   *innerhashkey;
@@ -220,14 +219,13 @@ ExecHashJoin(HashJoin *node)
 									  InvalidBuffer,
 									  false);	/* don't pfree this tuple */
 			econtext->ecxt_innertuple = inntuple;
-			qualResult = ExecQual(qual, econtext);
 			/* ----------------
 			 * if we pass the qual, then save state for next call and
 			 * have ExecProject form the projection, store it
 			 * in the tuple table, and return the slot.
 			 * ----------------
 			 */
-			if (qualResult)
+			if (ExecQual(qual, econtext, false))
 			{
 				ProjectionInfo *projInfo;
 				TupleTableSlot *result;
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index b9e3cf58636832608d01f386f0f8c31e3b6f6384..6ed14e0ad9adf00bc978da1af43178772fbf927e 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.43 1999/09/24 00:24:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.44 2000/01/19 23:54:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,7 +137,8 @@ IndexNext(IndexScan *node)
 		{
 			scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
 			if (ExecQual(nth(iptr, node->indxqualorig),
-						 scanstate->cstate.cs_ExprContext))
+						 scanstate->cstate.cs_ExprContext,
+						 false))
 				break;
 		}
 		if (iptr == numIndices) /* would not be returned by indices */
@@ -220,7 +221,8 @@ IndexNext(IndexScan *node)
 				{
 					scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
 					if (ExecQual(nth(prev_index, node->indxqualorig),
-								 scanstate->cstate.cs_ExprContext))
+								 scanstate->cstate.cs_ExprContext,
+								 false))
 					{
 						prev_matches = true;
 						break;
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index b15e135465d1b42bd29b6c936d4b1c5db800cc35..59287c0f509d815f5663d16ccf1020e99aba8b08 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.32 1999/11/22 17:56:03 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.33 2000/01/19 23:54:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -186,12 +186,12 @@ MJFormSkipQual(List *qualList, char *replaceopname)
  *		MergeCompare
  *
  *		Compare the keys according to 'compareQual' which is of the
- *		form: {(key1a > key2a)(key1b > key2b) ...}.
+ *		form: { (key1a > key2a) (key1b > key2b) ... }.
  *
- *		(actually, it could also be the form (key1a < key2a)..)
+ *		(actually, it could also be of the form (key1a < key2a)...)
  *
  *		This is different from calling ExecQual because ExecQual returns
- *		true only if ALL the comparisions clauses are satisfied.
+ *		true only if ALL the comparison clauses are satisfied.
  *		However, there is an order of significance among the keys with
  *		the first keys being most significant. Therefore, the clauses
  *		are evaluated in order and the 'compareQual' is satisfied
@@ -217,14 +217,14 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 
 	/* ----------------
 	 *	for each pair of clauses, test them until
-	 *	our compare conditions are satisified
+	 *	our compare conditions are satisfied
 	 * ----------------
 	 */
 	eqclause = eqQual;
 	foreach(clause, compareQual)
 	{
 		/* ----------------
-		 *	 first test if our compare clause is satisified.
+		 *	 first test if our compare clause is satisfied.
 		 *	 if so then return true. ignore isDone, don't iterate in
 		 *	 quals.
 		 * ----------------
@@ -255,7 +255,7 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 
 	/* ----------------
 	 *	if we get here then it means none of our key greater-than
-	 *	conditions were satisified so we return false.
+	 *	conditions were satisfied so we return false.
 	 * ----------------
 	 */
 	return false;
@@ -547,7 +547,7 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_JOINTEST:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
 
-				qualResult = ExecQual((List *) mergeclauses, econtext);
+				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
 				if (qualResult)
@@ -558,14 +558,14 @@ ExecMergeJoin(MergeJoin *node)
 
 				/*
 				 * EXEC_MJ_JOINTUPLES means we have two tuples which
-				 * satisified the merge clause so we join them and then
+				 * satisfied the merge clause so we join them and then
 				 * proceed to get the next inner tuple (EXEC_NEXT_INNER).
 				 */
 			case EXEC_MJ_JOINTUPLES:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
 				mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
 
-				qualResult = ExecQual((List *) qual, econtext);
+				qualResult = ExecQual((List *) qual, econtext, false);
 				MJ_DEBUG_QUAL(qual, qualResult);
 
 				if (qualResult)
@@ -693,7 +693,7 @@ ExecMergeJoin(MergeJoin *node)
 				innerTupleSlot = econtext->ecxt_innertuple;
 				econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
 
-				qualResult = ExecQual((List *) mergeclauses, econtext);
+				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
 				if (qualResult)
@@ -777,7 +777,7 @@ ExecMergeJoin(MergeJoin *node)
 				 *	we update the marked tuple and go join them.
 				 * ----------------
 				 */
-				qualResult = ExecQual((List *) mergeclauses, econtext);
+				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
 				if (qualResult)
@@ -886,7 +886,7 @@ ExecMergeJoin(MergeJoin *node)
 				 *	we update the marked tuple and go join them.
 				 * ----------------
 				 */
-				qualResult = ExecQual((List *) mergeclauses, econtext);
+				qualResult = ExecQual((List *) mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
 				if (qualResult)
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index a75e82c0b11895dd03100977af4587561de2bfd7..861df1d6e0fba7e46891acfd7cfabe5f7b041e4b 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.13 1999/07/16 04:58:51 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.14 2000/01/19 23:54:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,7 +65,6 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 	TupleTableSlot *innerTupleSlot;
 
 	List	   *qual;
-	bool		qualResult;
 	ExprContext *econtext;
 
 	/* ----------------
@@ -208,9 +207,8 @@ ExecNestLoop(NestLoop *node, Plan *parent)
 		 * ----------------
 		 */
 		ENL1_printf("testing qualification");
-		qualResult = ExecQual((List *) qual, econtext);
 
-		if (qualResult)
+		if (ExecQual((List *) qual, econtext, false))
 		{
 			/* ----------------
 			 *	qualification was satisified so we project and
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 7c8166c6f21e7e9d82ef3ac1908d1f1fccd13c89..ac63d2af425fd6b3440ab551468012e80114e7f8 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -27,7 +27,7 @@
  *				   SeqScan (emp.all)
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.11 1999/05/25 16:08:46 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.12 2000/01/19 23:54:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,7 +79,9 @@ ExecResult(Result *node)
 	 */
 	if (resstate->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual, econtext);
+		bool		qualResult = ExecQual((List *) node->resconstantqual,
+										  econtext,
+										  false);
 
 		resstate->rs_checkqual = false;
 		if (qualResult == false)
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index af599330a096617e4ec5765d22387a365a093289..88dcb741b3d63d8c0e92b895cff74909278c84de 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.40 1999/12/10 03:56:08 momjian Exp $
+ * $Id: executor.h,v 1.41 2000/01/19 23:55:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,17 +24,7 @@
  */
 /* return: true if tuple in slot is NULL, slot is slot to test */
 #define TupIsNull(slot) \
-( \
-	((slot) == NULL) ? \
-		true \
-	: \
-	( \
-		((slot)->val == NULL) ? \
-			true \
-		: \
-			false \
-	) \
-)
+	((slot) == NULL || (slot)->val == NULL)
 
 /*
  * prototypes from functions in execAmi.c
@@ -88,13 +78,12 @@ extern Datum ExecExtractResult(TupleTableSlot *slot, AttrNumber attnum,
 extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
 			  bool *isNull);
 
-/* stop here */
 extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
 				  bool *isNull);
 extern char *GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull);
 extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
 			 bool *isDone);
-extern bool ExecQual(List *qual, ExprContext *econtext);
+extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
 extern int	ExecTargetListLength(List *targetlist);
 extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
 
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index 5c39c825c1b9217fbfd9fd136ebb878966364fe4..6acef5eb24106eba3fd37c31fe886327bf3661ba 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -89,7 +89,6 @@ CREATE TABLE INSERT_TBL (x INT DEFAULT nextval('insert_seq'),
 	CONSTRAINT INSERT_CON CHECK (x >= 3 AND y <> 'check failed' AND x < 8),
 	CHECK (x + z = 0));
 
-INSERT INTO INSERT_TBL VALUES (null, null, null);
 INSERT INTO INSERT_TBL(x,z) VALUES (2, -2);
 
 SELECT '' AS zero, * FROM INSERT_TBL;
@@ -119,6 +118,13 @@ INSERT INTO INSERT_TBL(y) VALUES ('Y');
 
 SELECT 'eight' AS one, currval('insert_seq');
 
+-- According to SQL92, it is OK to insert a record that gives rise to NULL
+-- constraint-condition results.  Postgres used to reject this, but it
+-- was wrong:
+INSERT INTO INSERT_TBL VALUES (null, null, null);
+
+SELECT '' AS nine, * FROM INSERT_TBL;
+
 --
 -- Check inheritance of defaults and constraints
 --
@@ -166,7 +172,7 @@ DROP TABLE tmp;
 -- Check constraints on UPDATE
 --
 
-UPDATE INSERT_TBL SET x = NULL WHERE x = 6;
+UPDATE INSERT_TBL SET x = NULL WHERE x = 5;
 UPDATE INSERT_TBL SET x = 6 WHERE x = 6;
 UPDATE INSERT_TBL SET x = -z, z = -x;
 UPDATE INSERT_TBL SET x = z, z = x;
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index f1a107759df5da70592124487bd04f810f361b17..3b9bc2c56c0ea4e3ea4291010660e7d86e4269b8 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -106,8 +106,6 @@ CREATE TABLE INSERT_TBL (x INT DEFAULT nextval('insert_seq'),
 	z INT DEFAULT -1 * currval('insert_seq'),
 	CONSTRAINT INSERT_CON CHECK (x >= 3 AND y <> 'check failed' AND x < 8),
 	CHECK (x + z = 0));
-INSERT INTO INSERT_TBL VALUES (null, null, null);
-ERROR:  ExecAppend: rejected due to CHECK constraint $2
 INSERT INTO INSERT_TBL(x,z) VALUES (2, -2);
 ERROR:  ExecAppend: rejected due to CHECK constraint insert_con
 SELECT '' AS zero, * FROM INSERT_TBL;
@@ -171,6 +169,22 @@ SELECT 'eight' AS one, currval('insert_seq');
  eight |       8
 (1 row)
 
+-- According to SQL92, it is OK to insert a record that gives rise to NULL
+-- constraint-condition results.  Postgres used to reject this, but it
+-- was wrong:
+INSERT INTO INSERT_TBL VALUES (null, null, null);
+SELECT '' AS nine, * FROM INSERT_TBL;
+ nine | x |       y       | z  
+------+---+---------------+----
+      | 3 | Y             | -3
+      | 7 | -NULL-        | -7
+      | 7 | !check failed | -7
+      | 4 | -!NULL-       | -4
+      | 5 | !check failed | -5
+      | 6 | -!NULL-       | -6
+      |   |               |   
+(7 rows)
+
 --
 -- Check inheritance of defaults and constraints
 --
@@ -212,7 +226,6 @@ SELECT '' AS three, * FROM INSERT_TBL;
 (3 rows)
 
 INSERT INTO INSERT_TBL SELECT * FROM tmp WHERE yd = 'try again';
-ERROR:  ExecAppend: rejected due to CHECK constraint $2
 INSERT INTO INSERT_TBL(y,z) SELECT yd, -7 FROM tmp WHERE yd = 'try again';
 INSERT INTO INSERT_TBL(y,z) SELECT yd, -8 FROM tmp WHERE yd = 'try again';
 ERROR:  ExecAppend: rejected due to CHECK constraint insert_con
@@ -222,15 +235,15 @@ SELECT '' AS four, * FROM INSERT_TBL;
       | 4 | Y             | -4
       | 5 | !check failed | -5
       | 6 | try again     | -6
+      |   | try again     |   
       | 7 | try again     | -7
-(4 rows)
+(5 rows)
 
 DROP TABLE tmp;
 --
 -- Check constraints on UPDATE
 --
-UPDATE INSERT_TBL SET x = NULL WHERE x = 6;
-ERROR:  ExecReplace: rejected due to CHECK constraint $2
+UPDATE INSERT_TBL SET x = NULL WHERE x = 5;
 UPDATE INSERT_TBL SET x = 6 WHERE x = 6;
 UPDATE INSERT_TBL SET x = -z, z = -x;
 UPDATE INSERT_TBL SET x = z, z = x;
@@ -239,10 +252,11 @@ SELECT * FROM INSERT_TBL;
  x |       y       | z  
 ---+---------------+----
  4 | Y             | -4
- 5 | !check failed | -5
+   | try again     |   
  7 | try again     | -7
+ 5 | !check failed |   
  6 | try again     | -6
-(4 rows)
+(5 rows)
 
 -- DROP TABLE INSERT_TBL;
 --