mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
I found another bug in btree index. Looking at the code it seems that NULL keys are never used to build or scan a btree index (see the explain commands in the example). However this is not the case when a null key is retrieved in an outer loop of a join select and used in an index scan of an inner loop. This bug causes at least three kinds of problems: 1) the backend crashes when it tries to compare a text string with a null. 2) it is not possible to find tuples with null keys in a join. 3) null is considered equal to 0 when the datum is passed by value, see the last query. Submitted by: Massimo Dal Zotto <dz@cs.unitn.it>
156 lines
3.8 KiB
C
156 lines
3.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* heapvalid.c--
|
|
* heap tuple qualification validity checking code
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.7 1996/10/30 06:07:56 scrappy Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <time.h>
|
|
|
|
#include "postgres.h"
|
|
#include "fmgr.h"
|
|
#include "access/attnum.h"
|
|
#include "catalog/pg_am.h"
|
|
#include "catalog/pg_attribute.h"
|
|
#include "catalog/pg_class.h"
|
|
#include "nodes/nodes.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "storage/block.h"
|
|
#include "storage/buf.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/item.h"
|
|
#include "storage/itemid.h"
|
|
#include "storage/off.h"
|
|
#include "utils/nabstime.h"
|
|
|
|
#include "access/skey.h"
|
|
#include "access/tupdesc.h"
|
|
#include "access/xact.h"
|
|
#include "rewrite/prs2lock.h"
|
|
#include "storage/bufpage.h"
|
|
#include "storage/itemptr.h"
|
|
|
|
#include "access/strat.h"
|
|
|
|
#include "access/htup.h"
|
|
#include "utils/rel.h"
|
|
|
|
#include "access/heaptuple.h"
|
|
#include "utils/tqual.h"
|
|
|
|
/* ----------------
|
|
* heap_keytest
|
|
*
|
|
* Test a heap tuple with respect to a scan key.
|
|
* ----------------
|
|
*/
|
|
bool
|
|
heap_keytest(HeapTuple t,
|
|
TupleDesc tupdesc,
|
|
int nkeys,
|
|
ScanKey keys)
|
|
{
|
|
bool isnull;
|
|
Datum atp;
|
|
int test;
|
|
|
|
for (; nkeys--; keys++) {
|
|
atp = (Datum)heap_getattr(t, InvalidBuffer,
|
|
keys->sk_attno,
|
|
tupdesc,
|
|
&isnull);
|
|
|
|
if (isnull)
|
|
/* XXX eventually should check if SK_ISNULL */
|
|
return false;
|
|
|
|
if (keys->sk_flags & SK_ISNULL) {
|
|
return (false);
|
|
}
|
|
|
|
if (keys->sk_flags & SK_COMMUTE)
|
|
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
|
|
keys->sk_argument, atp);
|
|
else
|
|
test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
|
|
atp, keys->sk_argument);
|
|
|
|
if (!test == !(keys->sk_flags & SK_NEGATE))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ----------------
|
|
* heap_tuple_satisfies
|
|
*
|
|
* Returns a valid HeapTuple if it satisfies the timequal and keytest.
|
|
* Returns NULL otherwise. Used to be heap_satisifies (sic) which
|
|
* returned a boolean. It now returns a tuple so that we can avoid doing two
|
|
* PageGetItem's per tuple.
|
|
*
|
|
* Complete check of validity including LP_CTUP and keytest.
|
|
* This should perhaps be combined with valid somehow in the
|
|
* future. (Also, additional rule tests/time range tests.)
|
|
*
|
|
* on 8/21/92 mao says: i rearranged the tests here to do keytest before
|
|
* SatisfiesTimeQual. profiling indicated that even for vacuumed relations,
|
|
* time qual checking was more expensive than key testing. time qual is
|
|
* least likely to fail, too. we should really add the time qual test to
|
|
* the restriction and optimize it in the normal way. this has interactions
|
|
* with joey's expensive function work.
|
|
* ----------------
|
|
*/
|
|
HeapTuple
|
|
heap_tuple_satisfies(ItemId itemId,
|
|
Relation relation,
|
|
PageHeader disk_page,
|
|
TimeQual qual,
|
|
int nKeys,
|
|
ScanKey key)
|
|
{
|
|
HeapTuple tuple;
|
|
bool res;
|
|
|
|
if (! ItemIdIsUsed(itemId))
|
|
return NULL;
|
|
|
|
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
|
|
|
|
if (key != NULL)
|
|
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
|
|
nKeys, key);
|
|
else
|
|
res = TRUE;
|
|
|
|
if (res && (relation->rd_rel->relkind == RELKIND_UNCATALOGED
|
|
|| HeapTupleSatisfiesTimeQual(tuple,qual)))
|
|
return tuple;
|
|
|
|
return (HeapTuple) NULL;
|
|
}
|
|
|
|
/*
|
|
* TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
|
|
* already been updated once by the current transaction/command
|
|
* pair.
|
|
*/
|
|
bool
|
|
TupleUpdatedByCurXactAndCmd(HeapTuple t)
|
|
{
|
|
if (TransactionIdEquals(t->t_xmax,
|
|
GetCurrentTransactionId()) &&
|
|
t->t_cmax == GetCurrentCommandId())
|
|
return true;
|
|
|
|
return false;
|
|
}
|