Fix handling of NULL constraint conditions: per SQL92 spec, a NULL result
authorTom Lane
Wed, 19 Jan 2000 23:55:03 +0000 (23:55 +0000)
committerTom Lane
Wed, 19 Jan 2000 23:55:03 +0000 (23:55 +0000)
from a constraint condition does not violate the constraint (cf. discussion
on pghackers 12/9/99).  Implemented by adding a parameter to ExecQual,
specifying whether to return TRUE or FALSE when the qual result is
really NULL in three-valued boolean logic.  Currently, ExecRelCheck is
the only caller that asks for TRUE, but if we find any other places that
have the wrong response to NULL, it'll be easy to fix them.

20 files changed:
src/backend/access/gist/gist.c
src/backend/access/hash/hash.c
src/backend/access/nbtree/nbtree.c
src/backend/access/rtree/rtree.c
src/backend/catalog/index.c
src/backend/commands/copy.c
src/backend/executor/execMain.c
src/backend/executor/execQual.c
src/backend/executor/execScan.c
src/backend/executor/execUtils.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeNestloop.c
src/backend/executor/nodeResult.c
src/include/executor/executor.h
src/test/regress/input/constraints.source
src/test/regress/output/constraints.source

index 9c9e8aeee7f0b30104dc9c9819ba8241be590f8f..edebfcbebe3799b4446bfb1865952362da709aec 100644 (file)
@@ -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 */
        }
index fd3e3f94d50edbe36c328673f29282db206805c1..1d88bfc8bc49eaf65032cc7a40c148915d9984d0 100644 (file)
@@ -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 */
        }
index 458b427c70d17f7a9ee811449f0b86035235d660..1be5c08f973aeb7f8726013079e1265b260258d8 100644 (file)
@@ -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 */
        }
index 09930e1e340680637cd250cb73ce3544b5f4f82c..fd2a8e545a4e696d41b16faac3225ddf49f37f38 100644 (file)
@@ -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 */
        }
index 84009d6282b636656099bd0396db8d3432a16fff..375aed9a00658aa1106cbdcb0e161b6656288150 100644 (file)
@@ -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 */
index 328b2d644ebf5184757ec0887c3a679bcbd8ed35..60611398173310dcd60a48c519cf4372919081a6 100644 (file)
@@ -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 */
                    }
index fa98950ac1ff237606af513fbd5d04a360c1e3c1..863c13b64ece802a7242adf0a96bc07c1459f8a6 100644 (file)
@@ -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;
    }
 
index bd546597733dbb87350b8370b3c7bc87e192ee8c..9e9cbde83bfeffc84cc427e0ae2e322e2399d5a5 100644 (file)
@@ -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;
index 4196fb7a8a4dbd94dd6d8ad6a63ef887d07d7b86..4803653e14cd508fba8a832f02f8d507e06f1e8b 100644 (file)
@@ -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;
    }
 
index d168071e1fe9d17690bf82ccbbf6f7ffeaba98a7..4d5079ae6947eb7a869e1c2828fdd1ac028f5c47 100644 (file)
@@ -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;
        }
 
index 0a95c92347fb858ffd470245ef1203807364f51c..a40fd015af35ba43253c7d8d3ec64bcaaad7dc61 100644 (file)
@@ -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;
 }
index 1995048e2dbc8cef2077efd6a14b27e8d0db0091..46307a2aa963788f3deeac83c3d49805934e9882 100644 (file)
@@ -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;
index 9d5034307fe34a6c33cf9538bfe14bdee17dbacc..6f5d2cae1949777a8bcbba252284b6d3afd3ceb0 100644 (file)
@@ -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;
index b9e3cf58636832608d01f386f0f8c31e3b6f6384..6ed14e0ad9adf00bc978da1af43178772fbf927e 100644 (file)
@@ -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;
index b15e135465d1b42bd29b6c936d4b1c5db800cc35..59287c0f509d815f5663d16ccf1020e99aba8b08 100644 (file)
@@ -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)
index a75e82c0b11895dd03100977af4587561de2bfd7..861df1d6e0fba7e46891acfd7cfabe5f7b041e4b 100644 (file)
@@ -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
index 7c8166c6f21e7e9d82ef3ac1908d1f1fccd13c89..ac63d2af425fd6b3440ab551468012e80114e7f8 100644 (file)
@@ -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)
index af599330a096617e4ec5765d22387a365a093289..88dcb741b3d63d8c0e92b895cff74909278c84de 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
  */
 /* 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);
 
index 5c39c825c1b9217fbfd9fd136ebb878966364fe4..6acef5eb24106eba3fd37c31fe886327bf3661ba 100644 (file)
@@ -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;
index f1a107759df5da70592124487bd04f810f361b17..3b9bc2c56c0ea4e3ea4291010660e7d86e4269b8 100644 (file)
@@ -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;
 --