diff -Naur pgsql.virg/src/backend/executor/execQual.c pgsql.dev/src/backend/executor/execQual.c
--- pgsql.virg/src/backend/executor/execQual.c	Sat Jun 16 18:56:42 2001
+++ pgsql.dev/src/backend/executor/execQual.c	Mon Jun 18 06:15:59 2001
@@ -62,7 +62,8 @@
 static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
 static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone);
-
+static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
+			 bool *isNull, ExprDoneCond *isDone);
 
 /*----------
  *	  ExecEvalArrayRef
@@ -1092,6 +1093,46 @@
 }
 
 /* ----------------------------------------------------------------
+ *		ExecEvalNullTest
+ *
+ *		Evaluate a NullTest node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalNullTest(NullTest *ntest,
+					ExprContext *econtext,
+					bool *isNull,
+					ExprDoneCond *isDone)
+{
+	Datum		result;
+
+	result = ExecEvalExpr(ntest->arg, econtext, isNull, isDone);
+	if (*isNull && ntest->nulltesttype == IS_NULL)
+	{
+		*isNull = FALSE;
+		return BoolGetDatum(TRUE);
+	}
+	else if (*isNull && ntest->nulltesttype == IS_NOT_NULL)
+	{
+		*isNull = FALSE;
+		return BoolGetDatum(FALSE);
+	}
+	else if (!*isNull && ntest->nulltesttype == IS_NULL)
+	{
+		return BoolGetDatum(FALSE);
+	}
+	else if (!*isNull && ntest->nulltesttype == IS_NOT_NULL)
+	{
+		return BoolGetDatum(TRUE);
+	}
+	else									/* should never get here */ 
+	{
+		elog(ERROR, "NullTest node: something unexpected happened!");
+		return BoolGetDatum(NULL);
+	}
+}
+
+/* ----------------------------------------------------------------
  *		ExecEvalFieldSelect
  *
  *		Evaluate a FieldSelect node.
@@ -1262,6 +1303,12 @@
 			break;
 		case T_CaseExpr:
 			retDatum = ExecEvalCase((CaseExpr *) expression,
+									econtext,
+									isNull,
+									isDone);
+			break;
+		case T_NullTest:
+			retDatum = ExecEvalNullTest((NullTest *) expression,
 									econtext,
 									isNull,
 									isDone);
diff -Naur pgsql.virg/src/backend/nodes/copyfuncs.c pgsql.dev/src/backend/nodes/copyfuncs.c
--- pgsql.virg/src/backend/nodes/copyfuncs.c	Sat Jun 16 18:56:45 2001
+++ pgsql.dev/src/backend/nodes/copyfuncs.c	Mon Jun 18 05:11:36 2001
@@ -1017,6 +1017,24 @@
 	return newnode;
 }
 
+/* ----------------
+ *		_copyNullTest
+ * ----------------
+ */
+static NullTest *
+_copyNullTest(NullTest *from)
+{
+	NullTest *newnode = makeNode(NullTest);
+
+	/*
+	 * copy remainder of node
+	 */
+	Node_Copy(from, newnode, arg);
+	newnode->nulltesttype = from->nulltesttype;
+
+	return newnode;
+}
+
 static ArrayRef *
 _copyArrayRef(ArrayRef *from)
 {
@@ -2953,6 +2971,9 @@
 			break;
 		case T_CaseWhen:
 			retval = _copyCaseWhen(from);
+			break;
+		case T_NullTest:
+			retval = _copyNullTest(from);
 			break;
 		case T_FkConstraint:
 			retval = _copyFkConstraint(from);
diff -Naur pgsql.virg/src/backend/nodes/equalfuncs.c pgsql.dev/src/backend/nodes/equalfuncs.c
--- pgsql.virg/src/backend/nodes/equalfuncs.c	Sat Jun 16 18:56:45 2001
+++ pgsql.dev/src/backend/nodes/equalfuncs.c	Mon Jun 18 04:57:58 2001
@@ -1712,6 +1712,16 @@
 	return true;
 }
 
+static bool
+_equalNullTest(NullTest *a, NullTest *b)
+{
+	if (!equal(a->arg, b->arg))
+		return false;
+	if (a->nulltesttype != b->nulltesttype)
+		return false;
+	return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -2119,6 +2129,9 @@
 			break;
 		case T_CaseWhen:
 			retval = _equalCaseWhen(a, b);
+			break;
+		case T_NullTest:
+			retval = _equalNullTest(a, b);
 			break;
 		case T_FkConstraint:
 			retval = _equalFkConstraint(a, b);
diff -Naur pgsql.virg/src/backend/nodes/outfuncs.c pgsql.dev/src/backend/nodes/outfuncs.c
--- pgsql.virg/src/backend/nodes/outfuncs.c	Sat Jun 16 18:56:45 2001
+++ pgsql.dev/src/backend/nodes/outfuncs.c	Mon Jun 18 04:55:49 2001
@@ -1403,6 +1403,19 @@
 }
 
 /*
+ *	NullTest
+ */
+static void
+_outNullTest(StringInfo str, NullTest *node)
+{
+	appendStringInfo(str, " NULLTEST :arg ");
+	_outNode(str, node->arg);
+
+	appendStringInfo(str, " :nulltesttype %d ",
+				   node->nulltesttype);
+}
+
+/*
  * _outNode -
  *	  converts a Node into ascii string and append it to 'str'
  */
@@ -1639,7 +1652,9 @@
 			case T_CaseWhen:
 				_outCaseWhen(str, obj);
 				break;
-
+			case T_NullTest:
+				_outNullTest(str, obj);
+				break;
 			case T_VariableSetStmt:
 				break;
 			case T_SelectStmt:
diff -Naur pgsql.virg/src/backend/nodes/readfuncs.c pgsql.dev/src/backend/nodes/readfuncs.c
--- pgsql.virg/src/backend/nodes/readfuncs.c	Sat Jun 16 18:56:45 2001
+++ pgsql.dev/src/backend/nodes/readfuncs.c	Mon Jun 18 04:54:06 2001
@@ -860,6 +860,31 @@
 }
 
 /* ----------------
+ *		_readNullTest
+ *
+ *	NullTest is a subclass of Node
+ * ----------------
+ */
+static NullTest *
+_readNullTest(void)
+{
+	NullTest	*local_node;
+	char		*token;
+	int			length;
+
+	local_node = makeNode(NullTest);
+
+	token = pg_strtok(&length); /* eat :arg */
+	local_node->arg = nodeRead(true);	/* now read it */
+
+	token = pg_strtok(&length); /* eat :nulltesttype */
+	token = pg_strtok(&length); /* get nulltesttype */
+	local_node->nulltesttype = (NullTestType) atoi(token);
+
+	return local_node;
+}
+
+/* ----------------
  *		_readVar
  *
  *	Var is a subclass of Expr
@@ -1966,6 +1991,8 @@
 		return_value = _readCaseExpr();
 	else if (length == 4 && strncmp(token, "WHEN", length) == 0)
 		return_value = _readCaseWhen();
+	else if (length == 8 && strncmp(token, "NULLTEST", length) == 0)
+		return_value = _readNullTest();
 	else
 		elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
 
diff -Naur pgsql.virg/src/backend/optimizer/util/clauses.c pgsql.dev/src/backend/optimizer/util/clauses.c
--- pgsql.virg/src/backend/optimizer/util/clauses.c	Sat Jun 16 18:56:46 2001
+++ pgsql.dev/src/backend/optimizer/util/clauses.c	Mon Jun 18 04:50:10 2001
@@ -1580,6 +1580,8 @@
 					return true;
 			}
 			break;
+		case T_NullTest:
+			return walker(((NullTest *) node)->arg, context);
 		case T_SubLink:
 			{
 				SubLink    *sublink = (SubLink *) node;
@@ -1930,6 +1932,16 @@
 				FLATCOPY(newnode, casewhen, CaseWhen);
 				MUTATE(newnode->expr, casewhen->expr, Node *);
 				MUTATE(newnode->result, casewhen->result, Node *);
+				return (Node *) newnode;
+			}
+			break;
+		case T_NullTest:
+			{
+				NullTest *ntest = (NullTest *) node;
+				NullTest *newnode;
+
+				FLATCOPY(newnode, ntest, NullTest);
+				MUTATE(newnode->arg, ntest->arg, Node *);
 				return (Node *) newnode;
 			}
 			break;
diff -Naur pgsql.virg/src/backend/parser/gram.y pgsql.dev/src/backend/parser/gram.y
--- pgsql.virg/src/backend/parser/gram.y	Sat Jun 16 18:56:47 2001
+++ pgsql.dev/src/backend/parser/gram.y	Mon Jun 18 05:25:02 2001
@@ -4434,9 +4434,19 @@
 					 * (like Microsoft's).  Turn these into IS NULL exprs.
 					 */
 					if (exprIsNullConstant($3))
-						$$ = makeA_Expr(ISNULL, NULL, $1, NULL);
+						{
+							NullTest *n = makeNode(NullTest);
+							n->arg = $1;
+							n->nulltesttype = IS_NULL;
+							$$ = (Node *)n;
+						}
 					else if (exprIsNullConstant($1))
-						$$ = makeA_Expr(ISNULL, NULL, $3, NULL);
+						{
+							NullTest *n = makeNode(NullTest);
+							n->arg = $3;
+							n->nulltesttype = IS_NULL;
+							$$ = (Node *)n;
+						}
 					else
 						$$ = makeA_Expr(OP, "=", $1, $3);
 				}
@@ -4499,15 +4509,43 @@
 					n->agg_distinct = FALSE;
 					$$ = makeA_Expr(OP, "!~~*", $1, (Node *) n);
 				}
-
+		/* NullTest clause
+		 * Define SQL92-style Null test clause.
+		 * Allow all two forms described in the standard:
+		 *  a IS NULL
+		 *  a IS NOT NULL
+		 * Allow two SQL extensions
+		 *  a ISNULL
+		 *  a NOTNULL
+		 */
 		| a_expr ISNULL
-				{	$$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
+				{
+					NullTest *n = makeNode(NullTest);
+					n->arg = $1;
+					n->nulltesttype = IS_NULL;
+					$$ = (Node *)n;
+				}
 		| a_expr IS NULL_P
-				{	$$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
+				{
+					NullTest *n = makeNode(NullTest);
+					n->arg = $1;
+					n->nulltesttype = IS_NULL;
+					$$ = (Node *)n;
+				}
 		| a_expr NOTNULL
-				{	$$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
+				{
+					NullTest *n = makeNode(NullTest);
+					n->arg = $1;
+					n->nulltesttype = IS_NOT_NULL;
+					$$ = (Node *)n;
+				}
 		| a_expr IS NOT NULL_P
-				{	$$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
+				{
+					NullTest *n = makeNode(NullTest);
+					n->arg = $1;
+					n->nulltesttype = IS_NOT_NULL;
+					$$ = (Node *)n;
+				}
 		/* IS TRUE, IS FALSE, etc used to be function calls
 		 *  but let's make them expressions to allow the optimizer
 		 *  a chance to eliminate them if a_expr is a constant string.
diff -Naur pgsql.virg/src/backend/parser/parse_expr.c pgsql.dev/src/backend/parser/parse_expr.c
--- pgsql.virg/src/backend/parser/parse_expr.c	Sat Jun 16 18:56:47 2001
+++ pgsql.dev/src/backend/parser/parse_expr.c	Mon Jun 18 05:10:49 2001
@@ -510,6 +510,16 @@
 				break;
 			}
 
+		case T_NullTest:
+			{
+				NullTest   *n = (NullTest *) expr;
+
+				n->arg = transformExpr(pstate, n->arg, precedence);
+
+				result = (Node *) expr;
+				break;
+			}
+
 			/*
 			 * Quietly accept node types that may be presented when we are
 			 * called on an already-transformed tree.
@@ -668,6 +678,9 @@
 			break;
 		case T_CaseWhen:
 			type = exprType(((CaseWhen *) expr)->result);
+			break;
+		case T_NullTest:
+			type = BOOLOID;
 			break;
 		case T_Ident:
 			/* is this right? */
diff -Naur pgsql.virg/src/backend/utils/adt/ruleutils.c pgsql.dev/src/backend/utils/adt/ruleutils.c
--- pgsql.virg/src/backend/utils/adt/ruleutils.c	Sat Jun 16 18:56:48 2001
+++ pgsql.dev/src/backend/utils/adt/ruleutils.c	Sat Jun 16 23:36:15 2001
@@ -1891,6 +1891,21 @@
 			}
 			break;
 
+		case T_NullTest:
+			{
+				NullTest		*ntest = (NullTest *) node;
+
+				get_rule_expr(ntest->arg, context);
+				if (ntest->nulltesttype == IS_NULL)
+					appendStringInfo(buf, "%s", " IS NULL ");
+				else if (ntest->nulltesttype == IS_NOT_NULL)
+					appendStringInfo(buf, "%s", " IS NOT NULL ");
+				else	/* should never happen */
+					elog(ERROR, "invalid NullTest type %d",
+						 ntest->nulltesttype);
+			}
+			break;
+
 		case T_RelabelType:
 			{
 				RelabelType *relabel = (RelabelType *) node;
diff -Naur pgsql.virg/src/include/nodes/nodes.h pgsql.dev/src/include/nodes/nodes.h
--- pgsql.virg/src/include/nodes/nodes.h	Sat Jun 16 18:56:52 2001
+++ pgsql.dev/src/include/nodes/nodes.h	Mon Jun 18 04:35:25 2001
@@ -225,6 +225,7 @@
 	T_RowMarkXXX,				/* not used anymore; tag# available */
 	T_FkConstraint,
 	T_PrivGrantee,
+	T_NullTest,
 
 	/*
 	 * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
diff -Naur pgsql.virg/src/include/nodes/parsenodes.h pgsql.dev/src/include/nodes/parsenodes.h
--- pgsql.virg/src/include/nodes/parsenodes.h	Sat Jun 16 18:56:52 2001
+++ pgsql.dev/src/include/nodes/parsenodes.h	Mon Jun 18 04:39:00 2001
@@ -1054,6 +1054,28 @@
 	Node	   *result;			/* substitution result */
 } CaseWhen;
 
+/* ----------------
+ * NullTest
+ *
+ * NullTest represents the operation of comparing a value to a NULL
+ * value.  At runtime, the input expression is expected to be IS NULL,
+ * IS NOT NULL, ISNULL, or NOTNULL.
+ * The appropriate test is performed and returned as a Datum.
+ * ----------------
+ */
+
+typedef enum NullTestType
+{
+	IS_NULL, IS_NOT_NULL
+} NullTestType;
+
+typedef struct NullTest
+{
+	NodeTag			type;
+	Node			*arg;			/* input expression */
+	NullTestType	nulltesttype;	/* IS NULL, IS NOT NULL */
+} NullTest;
+
 /*
  * ColumnDef - column definition (used in various creates)
  *
