Fix handling of NULL constraint conditions: per SQL92 spec, a NULL result

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.
This commit is contained in:
Tom Lane 2000-01-19 23:55:03 +00:00
parent 08fb7375e3
commit 6d1efd76fb
20 changed files with 143 additions and 104 deletions

View 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 */
}

View 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 */
}

View 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 */
}

View 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 */
}

View 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 */

View 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 */
}

View 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;
}

View 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;

View 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;
}

View 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;
}

View 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;
}

View 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;

View 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;

View 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;

View 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)

View 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

View 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)

View 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 $
*
*-------------------------------------------------------------------------
*/
@ -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);

View 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;

View 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;
--