mirror of
https://github.com/postgres/postgres.git
synced 2025-11-01 21:31:19 +03:00
Massive commit to run PGINDENT on all *.c and *.h files.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* heapvalid.c--
|
||||
* heap tuple qualification validity checking code
|
||||
* 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.16 1997/08/29 09:12:20 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.17 1997/09/07 04:37:36 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -25,128 +25,138 @@
|
||||
#include <utils/builtins.h>
|
||||
|
||||
/* ----------------
|
||||
* heap_keytest
|
||||
* heap_keytest
|
||||
*
|
||||
* Test a heap tuple with respect to a scan key.
|
||||
* Test a heap tuple with respect to a scan key.
|
||||
* ----------------
|
||||
*/
|
||||
bool
|
||||
heap_keytest(HeapTuple t,
|
||||
TupleDesc tupdesc,
|
||||
int nkeys,
|
||||
ScanKey keys)
|
||||
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);
|
||||
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_func == (func_ptr) oideq) /* optimization */
|
||||
test = (keys->sk_argument == atp);
|
||||
else 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;
|
||||
}
|
||||
|
||||
if (keys->sk_func == (func_ptr)oideq) /* optimization */
|
||||
test = (keys->sk_argument == atp);
|
||||
else 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;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* heap_tuple_satisfies
|
||||
* 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.
|
||||
* 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.)
|
||||
* 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.
|
||||
* 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,
|
||||
Buffer buffer,
|
||||
PageHeader disk_page,
|
||||
TimeQual qual,
|
||||
int nKeys,
|
||||
ScanKey key)
|
||||
Relation relation,
|
||||
Buffer buffer,
|
||||
PageHeader disk_page,
|
||||
TimeQual qual,
|
||||
int nKeys,
|
||||
ScanKey key)
|
||||
{
|
||||
HeapTuple tuple, result;
|
||||
bool res;
|
||||
TransactionId old_tmin, old_tmax;
|
||||
HeapTuple tuple,
|
||||
result;
|
||||
bool res;
|
||||
TransactionId old_tmin,
|
||||
old_tmax;
|
||||
|
||||
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 (!ItemIdIsUsed(itemId))
|
||||
return NULL;
|
||||
|
||||
result = (HeapTuple)NULL;
|
||||
if (res) {
|
||||
if(relation->rd_rel->relkind == RELKIND_UNCATALOGED) {
|
||||
result = tuple;
|
||||
} else {
|
||||
old_tmin = tuple->t_tmin;
|
||||
old_tmax = tuple->t_tmax;
|
||||
res = HeapTupleSatisfiesTimeQual(tuple,qual);
|
||||
if(tuple->t_tmin != old_tmin ||
|
||||
tuple->t_tmax != old_tmax) {
|
||||
SetBufferCommitInfoNeedsSave(buffer);
|
||||
}
|
||||
if(res) {
|
||||
result = tuple;
|
||||
}
|
||||
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
|
||||
|
||||
if (key != NULL)
|
||||
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
|
||||
nKeys, key);
|
||||
else
|
||||
res = TRUE;
|
||||
|
||||
result = (HeapTuple) NULL;
|
||||
if (res)
|
||||
{
|
||||
if (relation->rd_rel->relkind == RELKIND_UNCATALOGED)
|
||||
{
|
||||
result = tuple;
|
||||
}
|
||||
else
|
||||
{
|
||||
old_tmin = tuple->t_tmin;
|
||||
old_tmax = tuple->t_tmax;
|
||||
res = HeapTupleSatisfiesTimeQual(tuple, qual);
|
||||
if (tuple->t_tmin != old_tmin ||
|
||||
tuple->t_tmax != old_tmax)
|
||||
{
|
||||
SetBufferCommitInfoNeedsSave(buffer);
|
||||
}
|
||||
if (res)
|
||||
{
|
||||
result = tuple;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
|
||||
* already been updated once by the current transaction/command
|
||||
* pair.
|
||||
* 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()) &&
|
||||
CommandIdGEScanCommandId (t->t_cmax))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
if (TransactionIdEquals(t->t_xmax,
|
||||
GetCurrentTransactionId()) &&
|
||||
CommandIdGEScanCommandId(t->t_cmax))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indextuple.c--
|
||||
* This file contains index tuple accessor and mutator routines,
|
||||
* as well as a few various tuple utilities.
|
||||
* This file contains index tuple accessor and mutator routines,
|
||||
* as well as a few various tuple utilities.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.15 1997/08/19 21:28:50 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.16 1997/09/07 04:37:37 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -21,402 +21,438 @@
|
||||
#include <access/tupmacs.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
static Size IndexInfoFindDataOffset(unsigned short t_info);
|
||||
static char *fastgetiattr(IndexTuple tup, int attnum,
|
||||
TupleDesc att, bool *isnull);
|
||||
static Size IndexInfoFindDataOffset(unsigned short t_info);
|
||||
static char *
|
||||
fastgetiattr(IndexTuple tup, int attnum,
|
||||
TupleDesc att, bool * isnull);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* index_ tuple interface routines
|
||||
* index_ tuple interface routines
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* index_formtuple
|
||||
* index_formtuple
|
||||
* ----------------
|
||||
*/
|
||||
IndexTuple
|
||||
index_formtuple(TupleDesc tupleDescriptor,
|
||||
Datum value[],
|
||||
char null[])
|
||||
Datum value[],
|
||||
char null[])
|
||||
{
|
||||
register char *tp; /* tuple pointer */
|
||||
IndexTuple tuple; /* return tuple */
|
||||
Size size, hoff;
|
||||
int i;
|
||||
unsigned short infomask = 0;
|
||||
bool hasnull = false;
|
||||
char tupmask = 0;
|
||||
int numberOfAttributes = tupleDescriptor->natts;
|
||||
|
||||
if (numberOfAttributes > MaxIndexAttributeNumber)
|
||||
elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
|
||||
numberOfAttributes, MaxIndexAttributeNumber);
|
||||
|
||||
|
||||
for (i = 0; i < numberOfAttributes && !hasnull; i++) {
|
||||
if (null[i] != ' ') hasnull = true;
|
||||
}
|
||||
|
||||
if (hasnull) infomask |= INDEX_NULL_MASK;
|
||||
|
||||
hoff = IndexInfoFindDataOffset(infomask);
|
||||
size = hoff
|
||||
+ ComputeDataSize(tupleDescriptor,
|
||||
value, null);
|
||||
size = DOUBLEALIGN(size); /* be conservative */
|
||||
|
||||
tp = (char *) palloc(size);
|
||||
tuple = (IndexTuple) tp;
|
||||
memset(tp,0,(int)size);
|
||||
|
||||
DataFill((char *)tp + hoff,
|
||||
tupleDescriptor,
|
||||
value,
|
||||
null,
|
||||
&tupmask,
|
||||
(hasnull ? (bits8*)tp + sizeof(*tuple) : NULL));
|
||||
|
||||
/*
|
||||
* We do this because DataFill wants to initialize a "tupmask" which
|
||||
* is used for HeapTuples, but we want an indextuple infomask. The only
|
||||
* "relevent" info is the "has variable attributes" field, which is in
|
||||
* mask position 0x02. We have already set the null mask above.
|
||||
*/
|
||||
|
||||
if (tupmask & 0x02) infomask |= INDEX_VAR_MASK;
|
||||
|
||||
/*
|
||||
* Here we make sure that we can actually hold the size. We also want
|
||||
* to make sure that size is not aligned oddly. This actually is a
|
||||
* rather odd way to make sure the size is not too large overall.
|
||||
*/
|
||||
|
||||
if (size & 0xE000)
|
||||
elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
|
||||
register char *tp; /* tuple pointer */
|
||||
IndexTuple tuple; /* return tuple */
|
||||
Size size,
|
||||
hoff;
|
||||
int i;
|
||||
unsigned short infomask = 0;
|
||||
bool hasnull = false;
|
||||
char tupmask = 0;
|
||||
int numberOfAttributes = tupleDescriptor->natts;
|
||||
|
||||
|
||||
infomask |= size;
|
||||
|
||||
/* ----------------
|
||||
* initialize metadata
|
||||
* ----------------
|
||||
*/
|
||||
tuple->t_info = infomask;
|
||||
return (tuple);
|
||||
if (numberOfAttributes > MaxIndexAttributeNumber)
|
||||
elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
|
||||
numberOfAttributes, MaxIndexAttributeNumber);
|
||||
|
||||
|
||||
for (i = 0; i < numberOfAttributes && !hasnull; i++)
|
||||
{
|
||||
if (null[i] != ' ')
|
||||
hasnull = true;
|
||||
}
|
||||
|
||||
if (hasnull)
|
||||
infomask |= INDEX_NULL_MASK;
|
||||
|
||||
hoff = IndexInfoFindDataOffset(infomask);
|
||||
size = hoff
|
||||
+ ComputeDataSize(tupleDescriptor,
|
||||
value, null);
|
||||
size = DOUBLEALIGN(size); /* be conservative */
|
||||
|
||||
tp = (char *) palloc(size);
|
||||
tuple = (IndexTuple) tp;
|
||||
memset(tp, 0, (int) size);
|
||||
|
||||
DataFill((char *) tp + hoff,
|
||||
tupleDescriptor,
|
||||
value,
|
||||
null,
|
||||
&tupmask,
|
||||
(hasnull ? (bits8 *) tp + sizeof(*tuple) : NULL));
|
||||
|
||||
/*
|
||||
* We do this because DataFill wants to initialize a "tupmask" which
|
||||
* is used for HeapTuples, but we want an indextuple infomask. The
|
||||
* only "relevent" info is the "has variable attributes" field, which
|
||||
* is in mask position 0x02. We have already set the null mask above.
|
||||
*/
|
||||
|
||||
if (tupmask & 0x02)
|
||||
infomask |= INDEX_VAR_MASK;
|
||||
|
||||
/*
|
||||
* Here we make sure that we can actually hold the size. We also want
|
||||
* to make sure that size is not aligned oddly. This actually is a
|
||||
* rather odd way to make sure the size is not too large overall.
|
||||
*/
|
||||
|
||||
if (size & 0xE000)
|
||||
elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
|
||||
|
||||
|
||||
infomask |= size;
|
||||
|
||||
/* ----------------
|
||||
* initialize metadata
|
||||
* ----------------
|
||||
*/
|
||||
tuple->t_info = infomask;
|
||||
return (tuple);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* fastgetiattr
|
||||
* fastgetiattr
|
||||
*
|
||||
* This is a newer version of fastgetiattr which attempts to be
|
||||
* faster by caching attribute offsets in the attribute descriptor.
|
||||
* This is a newer version of fastgetiattr which attempts to be
|
||||
* faster by caching attribute offsets in the attribute descriptor.
|
||||
*
|
||||
* an alternate way to speed things up would be to cache offsets
|
||||
* with the tuple, but that seems more difficult unless you take
|
||||
* the storage hit of actually putting those offsets into the
|
||||
* tuple you send to disk. Yuck.
|
||||
* an alternate way to speed things up would be to cache offsets
|
||||
* with the tuple, but that seems more difficult unless you take
|
||||
* the storage hit of actually putting those offsets into the
|
||||
* tuple you send to disk. Yuck.
|
||||
*
|
||||
* This scheme will be slightly slower than that, but should
|
||||
* preform well for queries which hit large #'s of tuples. After
|
||||
* you cache the offsets once, examining all the other tuples using
|
||||
* the same attribute descriptor will go much quicker. -cim 5/4/91
|
||||
* This scheme will be slightly slower than that, but should
|
||||
* preform well for queries which hit large #'s of tuples. After
|
||||
* you cache the offsets once, examining all the other tuples using
|
||||
* the same attribute descriptor will go much quicker. -cim 5/4/91
|
||||
* ----------------
|
||||
*/
|
||||
static char *
|
||||
static char *
|
||||
fastgetiattr(IndexTuple tup,
|
||||
int attnum,
|
||||
TupleDesc tupleDesc,
|
||||
bool *isnull)
|
||||
int attnum,
|
||||
TupleDesc tupleDesc,
|
||||
bool * isnull)
|
||||
{
|
||||
register char *tp; /* ptr to att in tuple */
|
||||
register char *bp = NULL; /* ptr to att in tuple */
|
||||
int slow; /* do we have to walk nulls? */
|
||||
register int data_off; /* tuple data offset */
|
||||
AttributeTupleForm *att = tupleDesc->attrs;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
Assert(PointerIsValid(isnull));
|
||||
Assert(attnum > 0);
|
||||
|
||||
/* ----------------
|
||||
* Three cases:
|
||||
*
|
||||
* 1: No nulls and no variable length attributes.
|
||||
* 2: Has a null or a varlena AFTER att.
|
||||
* 3: Has nulls or varlenas BEFORE att.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
*isnull = false;
|
||||
data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup :
|
||||
IndexInfoFindDataOffset(tup->t_info);
|
||||
|
||||
if (IndexTupleNoNulls(tup)) {
|
||||
|
||||
/* first attribute is always at position zero */
|
||||
|
||||
if (attnum == 1) {
|
||||
return(fetchatt(&(att[0]), (char *) tup + data_off));
|
||||
}
|
||||
attnum--;
|
||||
|
||||
if (att[attnum]->attcacheoff > 0) {
|
||||
return(fetchatt(&(att[attnum]),
|
||||
(char *) tup + data_off +
|
||||
att[attnum]->attcacheoff));
|
||||
}
|
||||
|
||||
tp = (char *) tup + data_off;
|
||||
|
||||
slow = 0;
|
||||
}else { /* there's a null somewhere in the tuple */
|
||||
|
||||
bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are here! */
|
||||
slow = 0;
|
||||
register char *tp; /* ptr to att in tuple */
|
||||
register char *bp = NULL; /* ptr to att in tuple */
|
||||
int slow; /* do we have to walk nulls? */
|
||||
register int data_off; /* tuple data offset */
|
||||
AttributeTupleForm *att = tupleDesc->attrs;
|
||||
|
||||
/* ----------------
|
||||
* check to see if desired att is null
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
attnum--;
|
||||
{
|
||||
if (att_isnull(attnum, bp)) {
|
||||
*isnull = true;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(PointerIsValid(isnull));
|
||||
Assert(attnum > 0);
|
||||
|
||||
/* ----------------
|
||||
* Now check to see if any preceeding bits are null...
|
||||
* Three cases:
|
||||
*
|
||||
* 1: No nulls and no variable length attributes.
|
||||
* 2: Has a null or a varlena AFTER att.
|
||||
* 3: Has nulls or varlenas BEFORE att.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
*isnull = false;
|
||||
data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup :
|
||||
IndexInfoFindDataOffset(tup->t_info);
|
||||
|
||||
if (IndexTupleNoNulls(tup))
|
||||
{
|
||||
register int i = 0; /* current offset in bp */
|
||||
register int mask; /* bit in byte we're looking at */
|
||||
register char n; /* current byte in bp */
|
||||
register int byte, finalbit;
|
||||
|
||||
byte = attnum >> 3;
|
||||
finalbit = attnum & 0x07;
|
||||
|
||||
for (; i <= byte; i++) {
|
||||
n = bp[i];
|
||||
if (i < byte) {
|
||||
/* check for nulls in any "earlier" bytes */
|
||||
if ((~n) != 0) {
|
||||
slow++;
|
||||
|
||||
/* first attribute is always at position zero */
|
||||
|
||||
if (attnum == 1)
|
||||
{
|
||||
return (fetchatt(&(att[0]), (char *) tup + data_off));
|
||||
}
|
||||
attnum--;
|
||||
|
||||
if (att[attnum]->attcacheoff > 0)
|
||||
{
|
||||
return (fetchatt(&(att[attnum]),
|
||||
(char *) tup + data_off +
|
||||
att[attnum]->attcacheoff));
|
||||
}
|
||||
|
||||
tp = (char *) tup + data_off;
|
||||
|
||||
slow = 0;
|
||||
}
|
||||
else
|
||||
{ /* there's a null somewhere in the tuple */
|
||||
|
||||
bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are
|
||||
* here! */
|
||||
slow = 0;
|
||||
/* ----------------
|
||||
* check to see if desired att is null
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
attnum--;
|
||||
{
|
||||
if (att_isnull(attnum, bp))
|
||||
{
|
||||
*isnull = true;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* ----------------
|
||||
* Now check to see if any preceeding bits are null...
|
||||
* ----------------
|
||||
*/
|
||||
{
|
||||
register int i = 0; /* current offset in bp */
|
||||
register int mask; /* bit in byte we're looking at */
|
||||
register char n; /* current byte in bp */
|
||||
register int byte,
|
||||
finalbit;
|
||||
|
||||
byte = attnum >> 3;
|
||||
finalbit = attnum & 0x07;
|
||||
|
||||
for (; i <= byte; i++)
|
||||
{
|
||||
n = bp[i];
|
||||
if (i < byte)
|
||||
{
|
||||
/* check for nulls in any "earlier" bytes */
|
||||
if ((~n) != 0)
|
||||
{
|
||||
slow++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check for nulls "before" final bit of last byte */
|
||||
mask = (finalbit << 1) - 1;
|
||||
if ((~n) & mask)
|
||||
slow++;
|
||||
}
|
||||
}
|
||||
}
|
||||
tp = (char *) tup + data_off;
|
||||
}
|
||||
|
||||
/* now check for any non-fixed length attrs before our attribute */
|
||||
|
||||
if (!slow)
|
||||
{
|
||||
if (att[attnum]->attcacheoff > 0)
|
||||
{
|
||||
return (fetchatt(&(att[attnum]),
|
||||
tp + att[attnum]->attcacheoff));
|
||||
}
|
||||
else if (!IndexTupleAllFixed(tup))
|
||||
{
|
||||
register int j = 0;
|
||||
|
||||
for (j = 0; j < attnum && !slow; j++)
|
||||
if (att[j]->attlen < 1)
|
||||
slow = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if slow is zero, and we got here, we know that we have a tuple with
|
||||
* no nulls. We also know that we have to initialize the remainder of
|
||||
* the attribute cached offset values.
|
||||
*/
|
||||
|
||||
if (!slow)
|
||||
{
|
||||
register int j = 1;
|
||||
register long off;
|
||||
|
||||
/*
|
||||
* need to set cache for some atts
|
||||
*/
|
||||
|
||||
att[0]->attcacheoff = 0;
|
||||
|
||||
while (att[j]->attcacheoff > 0)
|
||||
j++;
|
||||
|
||||
off = att[j - 1]->attcacheoff +
|
||||
att[j - 1]->attlen;
|
||||
|
||||
for (; j < attnum + 1; j++)
|
||||
{
|
||||
|
||||
/*
|
||||
* Fix me when going to a machine with more than a four-byte
|
||||
* word!
|
||||
*/
|
||||
|
||||
switch (att[j]->attlen)
|
||||
{
|
||||
case -1:
|
||||
off = (att[j]->attalign == 'd') ?
|
||||
DOUBLEALIGN(off) : INTALIGN(off);
|
||||
break;
|
||||
case sizeof(char):
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
off = INTALIGN(off);
|
||||
break;
|
||||
default:
|
||||
if (att[j]->attlen > sizeof(int32))
|
||||
off = (att[j]->attalign == 'd') ?
|
||||
DOUBLEALIGN(off) : LONGALIGN(off);
|
||||
else
|
||||
elog(WARN, "fastgetiattr: attribute %d has len %d",
|
||||
j, att[j]->attlen);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
att[j]->attcacheoff = off;
|
||||
off += att[j]->attlen;
|
||||
}
|
||||
|
||||
return (fetchatt(&(att[attnum]),
|
||||
tp + att[attnum]->attcacheoff));
|
||||
}
|
||||
else
|
||||
{
|
||||
register bool usecache = true;
|
||||
register int off = 0;
|
||||
register int i;
|
||||
|
||||
/*
|
||||
* Now we know that we have to walk the tuple CAREFULLY.
|
||||
*/
|
||||
|
||||
for (i = 0; i < attnum; i++)
|
||||
{
|
||||
if (!IndexTupleNoNulls(tup))
|
||||
{
|
||||
if (att_isnull(i, bp))
|
||||
{
|
||||
usecache = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (usecache && att[i]->attcacheoff > 0)
|
||||
{
|
||||
off = att[i]->attcacheoff;
|
||||
if (att[i]->attlen == -1)
|
||||
usecache = false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (usecache)
|
||||
att[i]->attcacheoff = off;
|
||||
switch (att[i]->attlen)
|
||||
{
|
||||
case sizeof(char):
|
||||
off++;
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off) +sizeof(short);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
off = INTALIGN(off) + sizeof(int32);
|
||||
break;
|
||||
case -1:
|
||||
usecache = false;
|
||||
off = (att[i]->attalign == 'd') ?
|
||||
DOUBLEALIGN(off) : INTALIGN(off);
|
||||
off += VARSIZE(tp + off);
|
||||
break;
|
||||
default:
|
||||
if (att[i]->attlen > sizeof(int32))
|
||||
off = (att[i]->attalign == 'd') ?
|
||||
DOUBLEALIGN(off) + att[i]->attlen :
|
||||
LONGALIGN(off) + att[i]->attlen;
|
||||
else
|
||||
elog(WARN, "fastgetiattr2: attribute %d has len %d",
|
||||
i, att[i]->attlen);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* I don't know why this code was missed here! I've got it from
|
||||
* heaptuple.c:fastgetattr(). - vadim 06/12/97
|
||||
*/
|
||||
switch (att[attnum]->attlen)
|
||||
{
|
||||
case -1:
|
||||
off = (att[attnum]->attalign == 'd') ?
|
||||
DOUBLEALIGN(off) : INTALIGN(off);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* check for nulls "before" final bit of last byte*/
|
||||
mask = (finalbit << 1) - 1;
|
||||
if ((~n) & mask)
|
||||
slow++;
|
||||
}
|
||||
}
|
||||
}
|
||||
tp = (char *) tup + data_off;
|
||||
}
|
||||
|
||||
/* now check for any non-fixed length attrs before our attribute */
|
||||
|
||||
if (!slow) {
|
||||
if (att[attnum]->attcacheoff > 0) {
|
||||
return(fetchatt(&(att[attnum]),
|
||||
tp + att[attnum]->attcacheoff));
|
||||
}else if (!IndexTupleAllFixed(tup)) {
|
||||
register int j = 0;
|
||||
|
||||
for (j = 0; j < attnum && !slow; j++)
|
||||
if (att[j]->attlen < 1) slow = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if slow is zero, and we got here, we know that we have a tuple with
|
||||
* no nulls. We also know that we have to initialize the remainder of
|
||||
* the attribute cached offset values.
|
||||
*/
|
||||
|
||||
if (!slow) {
|
||||
register int j = 1;
|
||||
register long off;
|
||||
|
||||
/*
|
||||
* need to set cache for some atts
|
||||
*/
|
||||
|
||||
att[0]->attcacheoff = 0;
|
||||
|
||||
while (att[j]->attcacheoff > 0) j++;
|
||||
|
||||
off = att[j-1]->attcacheoff +
|
||||
att[j-1]->attlen;
|
||||
|
||||
for (; j < attnum + 1; j++) {
|
||||
/*
|
||||
* Fix me when going to a machine with more than a four-byte
|
||||
* word!
|
||||
*/
|
||||
|
||||
switch(att[j]->attlen)
|
||||
{
|
||||
case -1:
|
||||
off = (att[j]->attalign=='d')?
|
||||
DOUBLEALIGN(off):INTALIGN(off);
|
||||
break;
|
||||
case sizeof(char):
|
||||
break;
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off);
|
||||
break;
|
||||
off = SHORTALIGN(off);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
off = INTALIGN(off);
|
||||
break;
|
||||
off = INTALIGN(off);
|
||||
break;
|
||||
default:
|
||||
if (att[j]->attlen > sizeof(int32))
|
||||
off = (att[j]->attalign=='d')?
|
||||
DOUBLEALIGN(off) : LONGALIGN(off);
|
||||
else
|
||||
elog(WARN, "fastgetiattr: attribute %d has len %d",
|
||||
j, att[j]->attlen);
|
||||
break;
|
||||
|
||||
if (att[attnum]->attlen < sizeof(int32))
|
||||
elog(WARN, "fastgetattr3: attribute %d has len %d",
|
||||
attnum, att[attnum]->attlen);
|
||||
if (att[attnum]->attalign == 'd')
|
||||
off = DOUBLEALIGN(off);
|
||||
else
|
||||
off = LONGALIGN(off);
|
||||
break;
|
||||
}
|
||||
|
||||
att[j]->attcacheoff = off;
|
||||
off += att[j]->attlen;
|
||||
|
||||
return (fetchatt(&att[attnum], tp + off));
|
||||
}
|
||||
|
||||
return(fetchatt( &(att[attnum]),
|
||||
tp + att[attnum]->attcacheoff));
|
||||
}else {
|
||||
register bool usecache = true;
|
||||
register int off = 0;
|
||||
register int i;
|
||||
|
||||
/*
|
||||
* Now we know that we have to walk the tuple CAREFULLY.
|
||||
*/
|
||||
|
||||
for (i = 0; i < attnum; i++) {
|
||||
if (!IndexTupleNoNulls(tup)) {
|
||||
if (att_isnull(i, bp)) {
|
||||
usecache = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (usecache && att[i]->attcacheoff > 0) {
|
||||
off = att[i]->attcacheoff;
|
||||
if (att[i]->attlen == -1)
|
||||
usecache = false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (usecache) att[i]->attcacheoff = off;
|
||||
switch(att[i]->attlen)
|
||||
{
|
||||
case sizeof(char):
|
||||
off++;
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off) + sizeof(short);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
off = INTALIGN(off) + sizeof(int32);
|
||||
break;
|
||||
case -1:
|
||||
usecache = false;
|
||||
off = (att[i]->attalign=='d')?
|
||||
DOUBLEALIGN(off):INTALIGN(off);
|
||||
off += VARSIZE(tp + off);
|
||||
break;
|
||||
default:
|
||||
if (att[i]->attlen > sizeof(int32))
|
||||
off = (att[i]->attalign=='d') ?
|
||||
DOUBLEALIGN(off) + att[i]->attlen :
|
||||
LONGALIGN(off) + att[i]->attlen;
|
||||
else
|
||||
elog(WARN, "fastgetiattr2: attribute %d has len %d",
|
||||
i, att[i]->attlen);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* I don't know why this code was missed here!
|
||||
* I've got it from heaptuple.c:fastgetattr().
|
||||
* - vadim 06/12/97
|
||||
*/
|
||||
switch (att[attnum]->attlen) {
|
||||
case -1:
|
||||
off = (att[attnum]->attalign=='d')?
|
||||
DOUBLEALIGN(off) : INTALIGN(off);
|
||||
break;
|
||||
case sizeof(char):
|
||||
break;
|
||||
case sizeof(short):
|
||||
off = SHORTALIGN(off);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
off = INTALIGN(off);
|
||||
break;
|
||||
default:
|
||||
if (att[attnum]->attlen < sizeof(int32))
|
||||
elog(WARN, "fastgetattr3: attribute %d has len %d",
|
||||
attnum, att[attnum]->attlen);
|
||||
if (att[attnum]->attalign == 'd')
|
||||
off = DOUBLEALIGN(off);
|
||||
else
|
||||
off = LONGALIGN(off);
|
||||
break;
|
||||
}
|
||||
|
||||
return(fetchatt(&att[attnum], tp + off));
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_getattr
|
||||
* index_getattr
|
||||
* ----------------
|
||||
*/
|
||||
Datum
|
||||
index_getattr(IndexTuple tuple,
|
||||
AttrNumber attNum,
|
||||
TupleDesc tupDesc,
|
||||
bool *isNullOutP)
|
||||
AttrNumber attNum,
|
||||
TupleDesc tupDesc,
|
||||
bool * isNullOutP)
|
||||
{
|
||||
Assert (attNum > 0);
|
||||
Assert(attNum > 0);
|
||||
|
||||
return (Datum)
|
||||
fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
|
||||
return (Datum)
|
||||
fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
|
||||
}
|
||||
|
||||
RetrieveIndexResult
|
||||
FormRetrieveIndexResult(ItemPointer indexItemPointer,
|
||||
ItemPointer heapItemPointer)
|
||||
ItemPointer heapItemPointer)
|
||||
{
|
||||
RetrieveIndexResult result;
|
||||
|
||||
Assert(ItemPointerIsValid(indexItemPointer));
|
||||
Assert(ItemPointerIsValid(heapItemPointer));
|
||||
|
||||
result = (RetrieveIndexResult) palloc(sizeof *result);
|
||||
|
||||
result->index_iptr = *indexItemPointer;
|
||||
result->heap_iptr = *heapItemPointer;
|
||||
|
||||
return (result);
|
||||
RetrieveIndexResult result;
|
||||
|
||||
Assert(ItemPointerIsValid(indexItemPointer));
|
||||
Assert(ItemPointerIsValid(heapItemPointer));
|
||||
|
||||
result = (RetrieveIndexResult) palloc(sizeof *result);
|
||||
|
||||
result->index_iptr = *indexItemPointer;
|
||||
result->heap_iptr = *heapItemPointer;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -425,19 +461,21 @@ FormRetrieveIndexResult(ItemPointer indexItemPointer,
|
||||
*
|
||||
* Change me if adding an attribute to IndexTuples!!!!!!!!!!!
|
||||
*/
|
||||
static Size
|
||||
static Size
|
||||
IndexInfoFindDataOffset(unsigned short t_info)
|
||||
{
|
||||
if (!(t_info & INDEX_NULL_MASK))
|
||||
return((Size) sizeof(IndexTupleData));
|
||||
else {
|
||||
Size size = sizeof(IndexTupleData);
|
||||
|
||||
if (t_info & INDEX_NULL_MASK) {
|
||||
size += sizeof(IndexAttributeBitMapData);
|
||||
if (!(t_info & INDEX_NULL_MASK))
|
||||
return ((Size) sizeof(IndexTupleData));
|
||||
else
|
||||
{
|
||||
Size size = sizeof(IndexTupleData);
|
||||
|
||||
if (t_info & INDEX_NULL_MASK)
|
||||
{
|
||||
size += sizeof(IndexAttributeBitMapData);
|
||||
}
|
||||
return DOUBLEALIGN(size); /* be conservative */
|
||||
}
|
||||
return DOUBLEALIGN(size); /* be conservative */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -445,17 +483,17 @@ IndexInfoFindDataOffset(unsigned short t_info)
|
||||
* we assume we have space that is already palloc'ed.
|
||||
*/
|
||||
void
|
||||
CopyIndexTuple(IndexTuple source, IndexTuple *target)
|
||||
CopyIndexTuple(IndexTuple source, IndexTuple * target)
|
||||
{
|
||||
Size size;
|
||||
IndexTuple ret;
|
||||
|
||||
size = IndexTupleSize(source);
|
||||
if (*target == NULL) {
|
||||
*target = (IndexTuple) palloc(size);
|
||||
}
|
||||
|
||||
ret = *target;
|
||||
memmove((char*)ret, (char*)source, size);
|
||||
}
|
||||
Size size;
|
||||
IndexTuple ret;
|
||||
|
||||
size = IndexTupleSize(source);
|
||||
if (*target == NULL)
|
||||
{
|
||||
*target = (IndexTuple) palloc(size);
|
||||
}
|
||||
|
||||
ret = *target;
|
||||
memmove((char *) ret, (char *) source, size);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indexvalid.c--
|
||||
* index tuple qualification validity checking code
|
||||
* index tuple qualification validity checking code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.14 1997/03/18 18:38:19 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.15 1997/09/07 04:37:38 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -21,64 +21,70 @@
|
||||
#include <executor/execdebug.h>
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* index scan key qualification code
|
||||
* index scan key qualification code
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int NIndexTupleProcessed;
|
||||
int NIndexTupleProcessed;
|
||||
|
||||
/* ----------------
|
||||
* index_keytest
|
||||
* index_keytest
|
||||
*
|
||||
* old comments
|
||||
* May eventually combine with other tests (like timeranges)?
|
||||
* Should have Buffer buffer; as an argument and pass it to amgetattr.
|
||||
* May eventually combine with other tests (like timeranges)?
|
||||
* Should have Buffer buffer; as an argument and pass it to amgetattr.
|
||||
* ----------------
|
||||
*/
|
||||
bool
|
||||
index_keytest(IndexTuple tuple,
|
||||
TupleDesc tupdesc,
|
||||
int scanKeySize,
|
||||
ScanKey key)
|
||||
TupleDesc tupdesc,
|
||||
int scanKeySize,
|
||||
ScanKey key)
|
||||
{
|
||||
bool isNull;
|
||||
Datum datum;
|
||||
int test;
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
while (scanKeySize > 0) {
|
||||
datum = index_getattr(tuple,
|
||||
key[0].sk_attno,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
|
||||
if (isNull) {
|
||||
/* XXX eventually should check if SK_ISNULL */
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_ISNULL) {
|
||||
return (false);
|
||||
bool isNull;
|
||||
Datum datum;
|
||||
int test;
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
while (scanKeySize > 0)
|
||||
{
|
||||
datum = index_getattr(tuple,
|
||||
key[0].sk_attno,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
|
||||
if (isNull)
|
||||
{
|
||||
/* XXX eventually should check if SK_ISNULL */
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_ISNULL)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE)
|
||||
{
|
||||
test = (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
datum) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
test = (*(key[0].sk_func))
|
||||
(datum,
|
||||
DatumGetPointer(key[0].sk_argument)) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
scanKeySize -= 1;
|
||||
key++;
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE) {
|
||||
test = (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
datum) ? 1 : 0;
|
||||
} else {
|
||||
test = (*(key[0].sk_func))
|
||||
(datum,
|
||||
DatumGetPointer(key[0].sk_argument)) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
scanKeySize -= 1;
|
||||
key++;
|
||||
}
|
||||
|
||||
return (true);
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* printtup.c--
|
||||
* Routines to print out tuples to the destination (binary or non-binary
|
||||
* portals, frontend/interactive backend, etc.).
|
||||
* Routines to print out tuples to the destination (binary or non-binary
|
||||
* portals, frontend/interactive backend, etc.).
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.15 1997/08/26 23:31:23 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.16 1997/09/07 04:37:39 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -16,279 +16,304 @@
|
||||
#include <string.h>
|
||||
#include <postgres.h>
|
||||
|
||||
#include <fmgr.h>
|
||||
#include <access/heapam.h>
|
||||
#include <access/printtup.h>
|
||||
#include <fmgr.h>
|
||||
#include <access/heapam.h>
|
||||
#include <access/printtup.h>
|
||||
#include <catalog/pg_type.h>
|
||||
#include <libpq/libpq.h>
|
||||
#include <utils/syscache.h>
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* printtup / debugtup support
|
||||
* printtup / debugtup support
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* typtoout - used by printtup and debugtup
|
||||
* typtoout - used by printtup and debugtup
|
||||
* ----------------
|
||||
*/
|
||||
Oid
|
||||
typtoout(Oid type)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
|
||||
typeTuple = SearchSysCacheTuple(TYPOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0, 0, 0);
|
||||
|
||||
if (HeapTupleIsValid(typeTuple))
|
||||
return((Oid)
|
||||
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
|
||||
|
||||
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
|
||||
return(InvalidOid);
|
||||
HeapTuple typeTuple;
|
||||
|
||||
typeTuple = SearchSysCacheTuple(TYPOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0, 0, 0);
|
||||
|
||||
if (HeapTupleIsValid(typeTuple))
|
||||
return ((Oid)
|
||||
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
|
||||
|
||||
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
|
||||
return (InvalidOid);
|
||||
}
|
||||
|
||||
Oid
|
||||
gettypelem(Oid type)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
|
||||
typeTuple = SearchSysCacheTuple(TYPOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0,0,0);
|
||||
|
||||
if (HeapTupleIsValid(typeTuple))
|
||||
return((Oid)
|
||||
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
|
||||
|
||||
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
|
||||
return(InvalidOid);
|
||||
HeapTuple typeTuple;
|
||||
|
||||
typeTuple = SearchSysCacheTuple(TYPOID,
|
||||
ObjectIdGetDatum(type),
|
||||
0, 0, 0);
|
||||
|
||||
if (HeapTupleIsValid(typeTuple))
|
||||
return ((Oid)
|
||||
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
|
||||
|
||||
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
|
||||
return (InvalidOid);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printtup
|
||||
* printtup
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
printtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
{
|
||||
int i, j, k;
|
||||
char *outputstr, *attr;
|
||||
bool isnull;
|
||||
Oid typoutput;
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("D", 1);
|
||||
|
||||
/* ----------------
|
||||
* send a bitmap of which attributes are null
|
||||
* ----------------
|
||||
*/
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
for (i = 0; i < tuple->t_natts; ) {
|
||||
i++; /* heap_getattr is a macro, so no increment */
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
j |= k;
|
||||
k >>= 1;
|
||||
if (!(i & 7)) {
|
||||
pq_putint(j, 1);
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
int i,
|
||||
j,
|
||||
k;
|
||||
char *outputstr,
|
||||
*attr;
|
||||
bool isnull;
|
||||
Oid typoutput;
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("D", 1);
|
||||
|
||||
/* ----------------
|
||||
* send a bitmap of which attributes are null
|
||||
* ----------------
|
||||
*/
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
for (i = 0; i < tuple->t_natts;)
|
||||
{
|
||||
i++; /* heap_getattr is a macro, so no
|
||||
* increment */
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
j |= k;
|
||||
k >>= 1;
|
||||
if (!(i & 7))
|
||||
{
|
||||
pq_putint(j, 1);
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i & 7)
|
||||
pq_putint(j, 1);
|
||||
|
||||
/* ----------------
|
||||
* send the attributes of this tuple
|
||||
* ----------------
|
||||
*/
|
||||
for (i = 0; i < tuple->t_natts; ++i) {
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
|
||||
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
|
||||
|
||||
if (!isnull && OidIsValid(typoutput)) {
|
||||
outputstr = fmgr(typoutput, attr,
|
||||
gettypelem(typeinfo->attrs[i]->atttypid));
|
||||
pq_putint(strlen(outputstr)+4, 4);
|
||||
pq_putnchar(outputstr, strlen(outputstr));
|
||||
pfree(outputstr);
|
||||
if (i & 7)
|
||||
pq_putint(j, 1);
|
||||
|
||||
/* ----------------
|
||||
* send the attributes of this tuple
|
||||
* ----------------
|
||||
*/
|
||||
for (i = 0; i < tuple->t_natts; ++i)
|
||||
{
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
|
||||
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
|
||||
|
||||
if (!isnull && OidIsValid(typoutput))
|
||||
{
|
||||
outputstr = fmgr(typoutput, attr,
|
||||
gettypelem(typeinfo->attrs[i]->atttypid));
|
||||
pq_putint(strlen(outputstr) + 4, 4);
|
||||
pq_putnchar(outputstr, strlen(outputstr));
|
||||
pfree(outputstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printatt
|
||||
* printatt
|
||||
* ----------------
|
||||
*/
|
||||
static void
|
||||
printatt(unsigned attributeId,
|
||||
AttributeTupleForm attributeP,
|
||||
char *value)
|
||||
AttributeTupleForm attributeP,
|
||||
char *value)
|
||||
{
|
||||
printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
|
||||
attributeId,
|
||||
attributeP->attname.data,
|
||||
value != NULL ? " = \"" : "",
|
||||
value != NULL ? value : "",
|
||||
value != NULL ? "\"" : "",
|
||||
(unsigned int) (attributeP->atttypid),
|
||||
attributeP->attlen,
|
||||
attributeP->attbyval ? 't' : 'f');
|
||||
printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
|
||||
attributeId,
|
||||
attributeP->attname.data,
|
||||
value != NULL ? " = \"" : "",
|
||||
value != NULL ? value : "",
|
||||
value != NULL ? "\"" : "",
|
||||
(unsigned int) (attributeP->atttypid),
|
||||
attributeP->attlen,
|
||||
attributeP->attbyval ? 't' : 'f');
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* showatts
|
||||
* showatts
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
showatts(char *name, TupleDesc tupleDesc)
|
||||
{
|
||||
int i;
|
||||
int natts = tupleDesc->natts;
|
||||
AttributeTupleForm *attinfo = tupleDesc->attrs;
|
||||
int i;
|
||||
int natts = tupleDesc->natts;
|
||||
AttributeTupleForm *attinfo = tupleDesc->attrs;
|
||||
|
||||
puts(name);
|
||||
for (i = 0; i < natts; ++i)
|
||||
printatt((unsigned) i+1, attinfo[i], (char *) NULL);
|
||||
printf("\t----\n");
|
||||
puts(name);
|
||||
for (i = 0; i < natts; ++i)
|
||||
printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
|
||||
printf("\t----\n");
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* debugtup
|
||||
* debugtup
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
debugtup(HeapTuple tuple, TupleDesc typeinfo)
|
||||
{
|
||||
register int i;
|
||||
char *attr, *value;
|
||||
bool isnull;
|
||||
Oid typoutput;
|
||||
|
||||
for (i = 0; i < tuple->t_natts; ++i) {
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
|
||||
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
|
||||
|
||||
if (!isnull && OidIsValid(typoutput)) {
|
||||
value = fmgr(typoutput, attr,
|
||||
gettypelem(typeinfo->attrs[i]->atttypid));
|
||||
printatt((unsigned) i+1, typeinfo->attrs[i], value);
|
||||
pfree(value);
|
||||
register int i;
|
||||
char *attr,
|
||||
*value;
|
||||
bool isnull;
|
||||
Oid typoutput;
|
||||
|
||||
for (i = 0; i < tuple->t_natts; ++i)
|
||||
{
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
|
||||
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
|
||||
|
||||
if (!isnull && OidIsValid(typoutput))
|
||||
{
|
||||
value = fmgr(typoutput, attr,
|
||||
gettypelem(typeinfo->attrs[i]->atttypid));
|
||||
printatt((unsigned) i + 1, typeinfo->attrs[i], value);
|
||||
pfree(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\t----\n");
|
||||
printf("\t----\n");
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* printtup_internal
|
||||
* Protocol expects either T, D, C, E, or N.
|
||||
* We use a different data prefix, e.g. 'B' instead of 'D' to
|
||||
* indicate a tuple in internal (binary) form.
|
||||
* printtup_internal
|
||||
* Protocol expects either T, D, C, E, or N.
|
||||
* We use a different data prefix, e.g. 'B' instead of 'D' to
|
||||
* indicate a tuple in internal (binary) form.
|
||||
*
|
||||
* This is same as printtup, except we don't use the typout func.
|
||||
* This is same as printtup, except we don't use the typout func.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
|
||||
{
|
||||
int i, j, k;
|
||||
char *attr;
|
||||
bool isnull;
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("B", 1);
|
||||
|
||||
/* ----------------
|
||||
* send a bitmap of which attributes are null
|
||||
* ----------------
|
||||
*/
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
for (i = 0; i < tuple->t_natts; ) {
|
||||
i++; /* heap_getattr is a macro, so no increment */
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
j |= k;
|
||||
k >>= 1;
|
||||
if (!(i & 7)) {
|
||||
pq_putint(j, 1);
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
}
|
||||
}
|
||||
if (i & 7)
|
||||
pq_putint(j, 1);
|
||||
|
||||
/* ----------------
|
||||
* send the attributes of this tuple
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
|
||||
#endif
|
||||
for (i = 0; i < tuple->t_natts; ++i) {
|
||||
int32 len = typeinfo->attrs[i]->attlen;
|
||||
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
|
||||
if (!isnull) {
|
||||
/* # of bytes, and opaque data */
|
||||
if (len == -1) {
|
||||
/* variable length, assume a varlena structure */
|
||||
len = VARSIZE(attr) - VARHDRSZ;
|
||||
|
||||
pq_putint(len, sizeof(int32));
|
||||
pq_putnchar(VARDATA(attr), len);
|
||||
#ifdef IPORTAL_DEBUG
|
||||
int i,
|
||||
j,
|
||||
k;
|
||||
char *attr;
|
||||
bool isnull;
|
||||
|
||||
/* ----------------
|
||||
* tell the frontend to expect new tuple data
|
||||
* ----------------
|
||||
*/
|
||||
pq_putnchar("B", 1);
|
||||
|
||||
/* ----------------
|
||||
* send a bitmap of which attributes are null
|
||||
* ----------------
|
||||
*/
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
for (i = 0; i < tuple->t_natts;)
|
||||
{
|
||||
i++; /* heap_getattr is a macro, so no
|
||||
* increment */
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
j |= k;
|
||||
k >>= 1;
|
||||
if (!(i & 7))
|
||||
{
|
||||
char *d = VARDATA(attr);
|
||||
|
||||
fprintf(stderr, "length %d data %x%x%x%x\n",
|
||||
len, *d, *(d+1), *(d+2), *(d+3));
|
||||
pq_putint(j, 1);
|
||||
j = 0;
|
||||
k = 1 << 7;
|
||||
}
|
||||
}
|
||||
if (i & 7)
|
||||
pq_putint(j, 1);
|
||||
|
||||
/* ----------------
|
||||
* send the attributes of this tuple
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
|
||||
#endif
|
||||
for (i = 0; i < tuple->t_natts; ++i)
|
||||
{
|
||||
int32 len = typeinfo->attrs[i]->attlen;
|
||||
|
||||
attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
|
||||
if (!isnull)
|
||||
{
|
||||
/* # of bytes, and opaque data */
|
||||
if (len == -1)
|
||||
{
|
||||
/* variable length, assume a varlena structure */
|
||||
len = VARSIZE(attr) - VARHDRSZ;
|
||||
|
||||
pq_putint(len, sizeof(int32));
|
||||
pq_putnchar(VARDATA(attr), len);
|
||||
#ifdef IPORTAL_DEBUG
|
||||
{
|
||||
char *d = VARDATA(attr);
|
||||
|
||||
fprintf(stderr, "length %d data %x%x%x%x\n",
|
||||
len, *d, *(d + 1), *(d + 2), *(d + 3));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fixed size */
|
||||
if (typeinfo->attrs[i]->attbyval)
|
||||
{
|
||||
int8 i8;
|
||||
int16 i16;
|
||||
int32 i32;
|
||||
|
||||
pq_putint(len, sizeof(int32));
|
||||
switch (len)
|
||||
{
|
||||
case sizeof(int8):
|
||||
i8 = DatumGetChar(attr);
|
||||
pq_putnchar((char *) &i8, len);
|
||||
break;
|
||||
case sizeof(int16):
|
||||
i16 = DatumGetInt16(attr);
|
||||
pq_putnchar((char *) &i16, len);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
i32 = DatumGetInt32(attr);
|
||||
pq_putnchar((char *) &i32, len);
|
||||
break;
|
||||
}
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "byval length %d data %d\n", len, attr);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
pq_putint(len, sizeof(int32));
|
||||
pq_putnchar(attr, len);
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "byref length %d data %x\n", len, attr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* fixed size */
|
||||
if (typeinfo->attrs[i]->attbyval) {
|
||||
int8 i8;
|
||||
int16 i16;
|
||||
int32 i32;
|
||||
|
||||
pq_putint(len, sizeof(int32));
|
||||
switch (len) {
|
||||
case sizeof(int8):
|
||||
i8 = DatumGetChar(attr);
|
||||
pq_putnchar((char *) &i8, len);
|
||||
break;
|
||||
case sizeof(int16):
|
||||
i16 = DatumGetInt16(attr);
|
||||
pq_putnchar((char *) &i16, len);
|
||||
break;
|
||||
case sizeof(int32):
|
||||
i32 = DatumGetInt32(attr);
|
||||
pq_putnchar((char *) &i32, len);
|
||||
break;
|
||||
}
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "byval length %d data %d\n", len, attr);
|
||||
#endif
|
||||
} else {
|
||||
pq_putint(len, sizeof(int32));
|
||||
pq_putnchar(attr, len);
|
||||
#ifdef IPORTAL_DEBUG
|
||||
fprintf(stderr, "byref length %d data %x\n", len, attr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* scan.c--
|
||||
* scan direction and key code
|
||||
* scan direction and key code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.9 1996/11/05 07:42:45 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.10 1997/09/07 04:37:39 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -19,49 +19,49 @@
|
||||
|
||||
/*
|
||||
* ScanKeyEntryIsLegal --
|
||||
* True iff the scan key entry is legal.
|
||||
* True iff the scan key entry is legal.
|
||||
*/
|
||||
#define ScanKeyEntryIsLegal(entry) \
|
||||
((bool) (AssertMacro(PointerIsValid(entry)) && \
|
||||
AttributeNumberIsValid(entry->sk_attno)))
|
||||
((bool) (AssertMacro(PointerIsValid(entry)) && \
|
||||
AttributeNumberIsValid(entry->sk_attno)))
|
||||
|
||||
/*
|
||||
* ScanKeyEntrySetIllegal --
|
||||
* Marks a scan key entry as illegal.
|
||||
* Marks a scan key entry as illegal.
|
||||
*/
|
||||
void
|
||||
ScanKeyEntrySetIllegal(ScanKey entry)
|
||||
{
|
||||
|
||||
Assert(PointerIsValid(entry));
|
||||
|
||||
entry->sk_flags = 0; /* just in case... */
|
||||
entry->sk_attno = InvalidAttrNumber;
|
||||
entry->sk_procedure = 0; /* should be InvalidRegProcedure */
|
||||
Assert(PointerIsValid(entry));
|
||||
|
||||
entry->sk_flags = 0; /* just in case... */
|
||||
entry->sk_attno = InvalidAttrNumber;
|
||||
entry->sk_procedure = 0; /* should be InvalidRegProcedure */
|
||||
}
|
||||
|
||||
/*
|
||||
* ScanKeyEntryInitialize --
|
||||
* Initializes an scan key entry.
|
||||
* Initializes an scan key entry.
|
||||
*
|
||||
* Note:
|
||||
* Assumes the scan key entry is valid.
|
||||
* Assumes the intialized scan key entry will be legal.
|
||||
* Assumes the scan key entry is valid.
|
||||
* Assumes the intialized scan key entry will be legal.
|
||||
*/
|
||||
void
|
||||
ScanKeyEntryInitialize(ScanKey entry,
|
||||
bits16 flags,
|
||||
AttrNumber attributeNumber,
|
||||
RegProcedure procedure,
|
||||
Datum argument)
|
||||
bits16 flags,
|
||||
AttrNumber attributeNumber,
|
||||
RegProcedure procedure,
|
||||
Datum argument)
|
||||
{
|
||||
Assert(PointerIsValid(entry));
|
||||
|
||||
entry->sk_flags = flags;
|
||||
entry->sk_attno = attributeNumber;
|
||||
entry->sk_procedure = procedure;
|
||||
entry->sk_argument = argument;
|
||||
fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
|
||||
|
||||
Assert(ScanKeyEntryIsLegal(entry));
|
||||
Assert(PointerIsValid(entry));
|
||||
|
||||
entry->sk_flags = flags;
|
||||
entry->sk_attno = attributeNumber;
|
||||
entry->sk_procedure = procedure;
|
||||
entry->sk_argument = argument;
|
||||
fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
|
||||
|
||||
Assert(ScanKeyEntryIsLegal(entry));
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tupdesc.c--
|
||||
* POSTGRES tuple descriptor support code
|
||||
* POSTGRES tuple descriptor support code
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.19 1997/08/22 02:55:39 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.20 1997/09/07 04:37:41 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* some of the executor utility code such as "ExecTypeFromTL" should be
|
||||
* moved here.
|
||||
* some of the executor utility code such as "ExecTypeFromTL" should be
|
||||
* moved here.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -28,518 +28,534 @@
|
||||
#include <utils/syscache.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTemplateTupleDesc
|
||||
* CreateTemplateTupleDesc
|
||||
*
|
||||
* This function allocates and zeros a tuple descriptor structure.
|
||||
* This function allocates and zeros a tuple descriptor structure.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTemplateTupleDesc(int natts)
|
||||
{
|
||||
uint32 size;
|
||||
TupleDesc desc;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(natts >= 1);
|
||||
|
||||
/* ----------------
|
||||
* allocate enough memory for the tuple descriptor and
|
||||
* zero it as TupleDescInitEntry assumes that the descriptor
|
||||
* is filled with NULL pointers.
|
||||
* ----------------
|
||||
*/
|
||||
size = natts * sizeof (AttributeTupleForm);
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->attrs = (AttributeTupleForm*) palloc(size);
|
||||
desc->constr = NULL;
|
||||
memset(desc->attrs, 0, size);
|
||||
uint32 size;
|
||||
TupleDesc desc;
|
||||
|
||||
desc->natts = natts;
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(natts >= 1);
|
||||
|
||||
return (desc);
|
||||
/* ----------------
|
||||
* allocate enough memory for the tuple descriptor and
|
||||
* zero it as TupleDescInitEntry assumes that the descriptor
|
||||
* is filled with NULL pointers.
|
||||
* ----------------
|
||||
*/
|
||||
size = natts * sizeof(AttributeTupleForm);
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->attrs = (AttributeTupleForm *) palloc(size);
|
||||
desc->constr = NULL;
|
||||
memset(desc->attrs, 0, size);
|
||||
|
||||
desc->natts = natts;
|
||||
|
||||
return (desc);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTupleDesc
|
||||
* CreateTupleDesc
|
||||
*
|
||||
* This function allocates a new TupleDesc from AttributeTupleForm array
|
||||
* This function allocates a new TupleDesc from AttributeTupleForm array
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTupleDesc(int natts, AttributeTupleForm* attrs)
|
||||
CreateTupleDesc(int natts, AttributeTupleForm * attrs)
|
||||
{
|
||||
TupleDesc desc;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(natts >= 1);
|
||||
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->attrs = attrs;
|
||||
desc->natts = natts;
|
||||
desc->constr = NULL;
|
||||
TupleDesc desc;
|
||||
|
||||
return (desc);
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(natts >= 1);
|
||||
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->attrs = attrs;
|
||||
desc->natts = natts;
|
||||
desc->constr = NULL;
|
||||
|
||||
return (desc);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTupleDescCopy
|
||||
* CreateTupleDescCopy
|
||||
*
|
||||
* This function creates a new TupleDesc by copying from an existing
|
||||
* TupleDesc
|
||||
*
|
||||
* !!! Constraints are not copied !!!
|
||||
* This function creates a new TupleDesc by copying from an existing
|
||||
* TupleDesc
|
||||
*
|
||||
* !!! Constraints are not copied !!!
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTupleDescCopy(TupleDesc tupdesc)
|
||||
{
|
||||
TupleDesc desc;
|
||||
int i, size;
|
||||
TupleDesc desc;
|
||||
int i,
|
||||
size;
|
||||
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->natts = tupdesc->natts;
|
||||
size = desc->natts * sizeof (AttributeTupleForm);
|
||||
desc->attrs = (AttributeTupleForm*) palloc(size);
|
||||
for (i=0;i<desc->natts;i++) {
|
||||
desc->attrs[i] =
|
||||
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
memmove(desc->attrs[i],
|
||||
tupdesc->attrs[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
desc->attrs[i]->attnotnull = false;
|
||||
desc->attrs[i]->atthasdef = false;
|
||||
}
|
||||
desc->constr = NULL;
|
||||
|
||||
return desc;
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->natts = tupdesc->natts;
|
||||
size = desc->natts * sizeof(AttributeTupleForm);
|
||||
desc->attrs = (AttributeTupleForm *) palloc(size);
|
||||
for (i = 0; i < desc->natts; i++)
|
||||
{
|
||||
desc->attrs[i] =
|
||||
(AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
memmove(desc->attrs[i],
|
||||
tupdesc->attrs[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
desc->attrs[i]->attnotnull = false;
|
||||
desc->attrs[i]->atthasdef = false;
|
||||
}
|
||||
desc->constr = NULL;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* CreateTupleDescCopyConstr
|
||||
* CreateTupleDescCopyConstr
|
||||
*
|
||||
* This function creates a new TupleDesc by copying from an existing
|
||||
* TupleDesc (with Constraints)
|
||||
*
|
||||
* This function creates a new TupleDesc by copying from an existing
|
||||
* TupleDesc (with Constraints)
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
||||
{
|
||||
TupleDesc desc;
|
||||
TupleConstr *constr = tupdesc->constr;
|
||||
int i, size;
|
||||
TupleDesc desc;
|
||||
TupleConstr *constr = tupdesc->constr;
|
||||
int i,
|
||||
size;
|
||||
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->natts = tupdesc->natts;
|
||||
size = desc->natts * sizeof (AttributeTupleForm);
|
||||
desc->attrs = (AttributeTupleForm*) palloc(size);
|
||||
for (i=0;i<desc->natts;i++) {
|
||||
desc->attrs[i] =
|
||||
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
memmove(desc->attrs[i],
|
||||
tupdesc->attrs[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
}
|
||||
if (constr)
|
||||
{
|
||||
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
|
||||
|
||||
cpy->has_not_null = constr->has_not_null;
|
||||
|
||||
if ( ( cpy->num_defval = constr->num_defval ) > 0 )
|
||||
{
|
||||
cpy->defval = (AttrDefault *) palloc (cpy->num_defval * sizeof (AttrDefault));
|
||||
memcpy (cpy->defval, constr->defval, cpy->num_defval * sizeof (AttrDefault));
|
||||
for (i = cpy->num_defval - 1; i >= 0; i--)
|
||||
{
|
||||
if ( constr->defval[i].adbin )
|
||||
cpy->defval[i].adbin = pstrdup (constr->defval[i].adbin);
|
||||
if ( constr->defval[i].adsrc )
|
||||
cpy->defval[i].adsrc = pstrdup (constr->defval[i].adsrc);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( cpy->num_check = constr->num_check ) > 0 )
|
||||
{
|
||||
cpy->check = (ConstrCheck *) palloc (cpy->num_check * sizeof (ConstrCheck));
|
||||
memcpy (cpy->check, constr->check, cpy->num_check * sizeof (ConstrCheck));
|
||||
for (i = cpy->num_check - 1; i >= 0; i--)
|
||||
{
|
||||
if ( constr->check[i].ccname )
|
||||
cpy->check[i].ccname = pstrdup (constr->check[i].ccname);
|
||||
if ( constr->check[i].ccbin )
|
||||
cpy->check[i].ccbin = pstrdup (constr->check[i].ccbin);
|
||||
if ( constr->check[i].ccsrc )
|
||||
cpy->check[i].ccsrc = pstrdup (constr->check[i].ccsrc);
|
||||
}
|
||||
}
|
||||
|
||||
desc->constr = cpy;
|
||||
}
|
||||
else
|
||||
desc->constr = NULL;
|
||||
|
||||
return desc;
|
||||
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
|
||||
desc->natts = tupdesc->natts;
|
||||
size = desc->natts * sizeof(AttributeTupleForm);
|
||||
desc->attrs = (AttributeTupleForm *) palloc(size);
|
||||
for (i = 0; i < desc->natts; i++)
|
||||
{
|
||||
desc->attrs[i] =
|
||||
(AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
memmove(desc->attrs[i],
|
||||
tupdesc->attrs[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
}
|
||||
if (constr)
|
||||
{
|
||||
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
|
||||
|
||||
cpy->has_not_null = constr->has_not_null;
|
||||
|
||||
if ((cpy->num_defval = constr->num_defval) > 0)
|
||||
{
|
||||
cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
|
||||
memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
|
||||
for (i = cpy->num_defval - 1; i >= 0; i--)
|
||||
{
|
||||
if (constr->defval[i].adbin)
|
||||
cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
|
||||
if (constr->defval[i].adsrc)
|
||||
cpy->defval[i].adsrc = pstrdup(constr->defval[i].adsrc);
|
||||
}
|
||||
}
|
||||
|
||||
if ((cpy->num_check = constr->num_check) > 0)
|
||||
{
|
||||
cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
|
||||
memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
|
||||
for (i = cpy->num_check - 1; i >= 0; i--)
|
||||
{
|
||||
if (constr->check[i].ccname)
|
||||
cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
|
||||
if (constr->check[i].ccbin)
|
||||
cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
|
||||
if (constr->check[i].ccsrc)
|
||||
cpy->check[i].ccsrc = pstrdup(constr->check[i].ccsrc);
|
||||
}
|
||||
}
|
||||
|
||||
desc->constr = cpy;
|
||||
}
|
||||
else
|
||||
desc->constr = NULL;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
void
|
||||
FreeTupleDesc (TupleDesc tupdesc)
|
||||
FreeTupleDesc(TupleDesc tupdesc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tupdesc->natts; i++)
|
||||
pfree (tupdesc->attrs[i]);
|
||||
pfree (tupdesc->attrs);
|
||||
if ( tupdesc->constr )
|
||||
{
|
||||
if ( tupdesc->constr->num_defval > 0 )
|
||||
{
|
||||
AttrDefault *attrdef = tupdesc->constr->defval;
|
||||
|
||||
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
|
||||
{
|
||||
if ( attrdef[i].adbin )
|
||||
pfree (attrdef[i].adbin);
|
||||
if ( attrdef[i].adsrc )
|
||||
pfree (attrdef[i].adsrc);
|
||||
}
|
||||
pfree (attrdef);
|
||||
}
|
||||
if ( tupdesc->constr->num_check > 0 )
|
||||
{
|
||||
ConstrCheck *check = tupdesc->constr->check;
|
||||
|
||||
for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
|
||||
{
|
||||
if ( check[i].ccname )
|
||||
pfree (check[i].ccname);
|
||||
if ( check[i].ccbin )
|
||||
pfree (check[i].ccbin);
|
||||
if ( check[i].ccsrc )
|
||||
pfree (check[i].ccsrc);
|
||||
}
|
||||
pfree (check);
|
||||
}
|
||||
pfree (tupdesc->constr);
|
||||
}
|
||||
|
||||
pfree (tupdesc);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tupdesc->natts; i++)
|
||||
pfree(tupdesc->attrs[i]);
|
||||
pfree(tupdesc->attrs);
|
||||
if (tupdesc->constr)
|
||||
{
|
||||
if (tupdesc->constr->num_defval > 0)
|
||||
{
|
||||
AttrDefault *attrdef = tupdesc->constr->defval;
|
||||
|
||||
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
|
||||
{
|
||||
if (attrdef[i].adbin)
|
||||
pfree(attrdef[i].adbin);
|
||||
if (attrdef[i].adsrc)
|
||||
pfree(attrdef[i].adsrc);
|
||||
}
|
||||
pfree(attrdef);
|
||||
}
|
||||
if (tupdesc->constr->num_check > 0)
|
||||
{
|
||||
ConstrCheck *check = tupdesc->constr->check;
|
||||
|
||||
for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
|
||||
{
|
||||
if (check[i].ccname)
|
||||
pfree(check[i].ccname);
|
||||
if (check[i].ccbin)
|
||||
pfree(check[i].ccbin);
|
||||
if (check[i].ccsrc)
|
||||
pfree(check[i].ccsrc);
|
||||
}
|
||||
pfree(check);
|
||||
}
|
||||
pfree(tupdesc->constr);
|
||||
}
|
||||
|
||||
pfree(tupdesc);
|
||||
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TupleDescInitEntry
|
||||
* TupleDescInitEntry
|
||||
*
|
||||
* This function initializes a single attribute structure in
|
||||
* a preallocated tuple descriptor.
|
||||
* This function initializes a single attribute structure in
|
||||
* a preallocated tuple descriptor.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
TupleDescInitEntry(TupleDesc desc,
|
||||
AttrNumber attributeNumber,
|
||||
char *attributeName,
|
||||
char *typeName,
|
||||
int attdim,
|
||||
bool attisset)
|
||||
AttrNumber attributeNumber,
|
||||
char *attributeName,
|
||||
char *typeName,
|
||||
int attdim,
|
||||
bool attisset)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
TypeTupleForm typeForm;
|
||||
AttributeTupleForm att;
|
||||
|
||||
/* ----------------
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
AssertArg(PointerIsValid(desc));
|
||||
AssertArg(attributeNumber >= 1);
|
||||
/* attributeName's are sometimes NULL,
|
||||
from resdom's. I don't know why that is, though -- Jolly */
|
||||
/* AssertArg(NameIsValid(attributeName));*/
|
||||
/* AssertArg(NameIsValid(typeName));*/
|
||||
|
||||
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
|
||||
|
||||
HeapTuple tuple;
|
||||
TypeTupleForm typeForm;
|
||||
AttributeTupleForm att;
|
||||
|
||||
/* ----------------
|
||||
* allocate storage for this attribute
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
desc->attrs[attributeNumber - 1] = att;
|
||||
|
||||
/* ----------------
|
||||
* initialize some of the attribute fields
|
||||
* ----------------
|
||||
*/
|
||||
att->attrelid = 0; /* dummy value */
|
||||
|
||||
if (attributeName != NULL)
|
||||
namestrcpy(&(att->attname), attributeName);
|
||||
else
|
||||
memset(att->attname.data,0,NAMEDATALEN);
|
||||
|
||||
|
||||
att->attdisbursion = 0; /* dummy value */
|
||||
att->attcacheoff = -1;
|
||||
|
||||
att->attnum = attributeNumber;
|
||||
att->attnelems = attdim;
|
||||
att->attisset = attisset;
|
||||
|
||||
att->attnotnull = false;
|
||||
att->atthasdef = false;
|
||||
|
||||
/* ----------------
|
||||
* search the system cache for the type tuple of the attribute
|
||||
* we are creating so that we can get the typeid and some other
|
||||
* stuff.
|
||||
*
|
||||
* Note: in the special case of
|
||||
*
|
||||
* create EMP (name = char16, manager = EMP)
|
||||
*
|
||||
* RelationNameCreateHeapRelation() calls BuildDesc() which
|
||||
* calls this routine and since EMP does not exist yet, the
|
||||
* system cache lookup below fails. That's fine, but rather
|
||||
* then doing a elog(WARN) we just leave that information
|
||||
* uninitialized, return false, then fix things up later.
|
||||
* -cim 6/14/90
|
||||
* ----------------
|
||||
*/
|
||||
tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
|
||||
0,0,0);
|
||||
if (! HeapTupleIsValid(tuple)) {
|
||||
/* ----------------
|
||||
* here type info does not exist yet so we just fill
|
||||
* the attribute with dummy information and return false.
|
||||
* sanity checks
|
||||
* ----------------
|
||||
*/
|
||||
att->atttypid = InvalidOid;
|
||||
att->attlen = (int16) 0;
|
||||
att->attbyval = (bool) 0;
|
||||
att->attalign = 'i';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* type info exists so we initialize our attribute
|
||||
* information from the type tuple we found..
|
||||
* ----------------
|
||||
*/
|
||||
typeForm = (TypeTupleForm) GETSTRUCT(tuple);
|
||||
|
||||
att->atttypid = tuple->t_oid;
|
||||
att->attalign = typeForm->typalign;
|
||||
|
||||
/* ------------------------
|
||||
If this attribute is a set, what is really stored in the
|
||||
attribute is the OID of a tuple in the pg_proc catalog.
|
||||
The pg_proc tuple contains the query string which defines
|
||||
this set - i.e., the query to run to get the set.
|
||||
So the atttypid (just assigned above) refers to the type returned
|
||||
by this query, but the actual length of this attribute is the
|
||||
length (size) of an OID.
|
||||
|
||||
Why not just make the atttypid point to the OID type, instead
|
||||
of the type the query returns? Because the executor uses the atttypid
|
||||
to tell the front end what type will be returned (in BeginCommand),
|
||||
and in the end the type returned will be the result of the query, not
|
||||
an OID.
|
||||
|
||||
Why not wait until the return type of the set is known (i.e., the
|
||||
recursive call to the executor to execute the set has returned)
|
||||
before telling the front end what the return type will be? Because
|
||||
the executor is a delicate thing, and making sure that the correct
|
||||
order of front-end commands is maintained is messy, especially
|
||||
considering that target lists may change as inherited attributes
|
||||
are considered, etc. Ugh.
|
||||
-----------------------------------------
|
||||
*/
|
||||
if (attisset) {
|
||||
Type t = type("oid");
|
||||
att->attlen = tlen(t);
|
||||
att->attbyval = tbyval(t);
|
||||
} else {
|
||||
att->attlen = typeForm->typlen;
|
||||
att->attbyval = typeForm->typbyval;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
AssertArg(PointerIsValid(desc));
|
||||
AssertArg(attributeNumber >= 1);
|
||||
|
||||
/*
|
||||
* attributeName's are sometimes NULL, from resdom's. I don't know
|
||||
* why that is, though -- Jolly
|
||||
*/
|
||||
/* AssertArg(NameIsValid(attributeName));*/
|
||||
/* AssertArg(NameIsValid(typeName));*/
|
||||
|
||||
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
|
||||
|
||||
|
||||
/* ----------------
|
||||
* allocate storage for this attribute
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
desc->attrs[attributeNumber - 1] = att;
|
||||
|
||||
/* ----------------
|
||||
* initialize some of the attribute fields
|
||||
* ----------------
|
||||
*/
|
||||
att->attrelid = 0; /* dummy value */
|
||||
|
||||
if (attributeName != NULL)
|
||||
namestrcpy(&(att->attname), attributeName);
|
||||
else
|
||||
memset(att->attname.data, 0, NAMEDATALEN);
|
||||
|
||||
|
||||
att->attdisbursion = 0; /* dummy value */
|
||||
att->attcacheoff = -1;
|
||||
|
||||
att->attnum = attributeNumber;
|
||||
att->attnelems = attdim;
|
||||
att->attisset = attisset;
|
||||
|
||||
att->attnotnull = false;
|
||||
att->atthasdef = false;
|
||||
|
||||
/* ----------------
|
||||
* search the system cache for the type tuple of the attribute
|
||||
* we are creating so that we can get the typeid and some other
|
||||
* stuff.
|
||||
*
|
||||
* Note: in the special case of
|
||||
*
|
||||
* create EMP (name = char16, manager = EMP)
|
||||
*
|
||||
* RelationNameCreateHeapRelation() calls BuildDesc() which
|
||||
* calls this routine and since EMP does not exist yet, the
|
||||
* system cache lookup below fails. That's fine, but rather
|
||||
* then doing a elog(WARN) we just leave that information
|
||||
* uninitialized, return false, then fix things up later.
|
||||
* -cim 6/14/90
|
||||
* ----------------
|
||||
*/
|
||||
tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
/* ----------------
|
||||
* here type info does not exist yet so we just fill
|
||||
* the attribute with dummy information and return false.
|
||||
* ----------------
|
||||
*/
|
||||
att->atttypid = InvalidOid;
|
||||
att->attlen = (int16) 0;
|
||||
att->attbyval = (bool) 0;
|
||||
att->attalign = 'i';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* type info exists so we initialize our attribute
|
||||
* information from the type tuple we found..
|
||||
* ----------------
|
||||
*/
|
||||
typeForm = (TypeTupleForm) GETSTRUCT(tuple);
|
||||
|
||||
att->atttypid = tuple->t_oid;
|
||||
att->attalign = typeForm->typalign;
|
||||
|
||||
/* ------------------------
|
||||
If this attribute is a set, what is really stored in the
|
||||
attribute is the OID of a tuple in the pg_proc catalog.
|
||||
The pg_proc tuple contains the query string which defines
|
||||
this set - i.e., the query to run to get the set.
|
||||
So the atttypid (just assigned above) refers to the type returned
|
||||
by this query, but the actual length of this attribute is the
|
||||
length (size) of an OID.
|
||||
|
||||
Why not just make the atttypid point to the OID type, instead
|
||||
of the type the query returns? Because the executor uses the atttypid
|
||||
to tell the front end what type will be returned (in BeginCommand),
|
||||
and in the end the type returned will be the result of the query, not
|
||||
an OID.
|
||||
|
||||
Why not wait until the return type of the set is known (i.e., the
|
||||
recursive call to the executor to execute the set has returned)
|
||||
before telling the front end what the return type will be? Because
|
||||
the executor is a delicate thing, and making sure that the correct
|
||||
order of front-end commands is maintained is messy, especially
|
||||
considering that target lists may change as inherited attributes
|
||||
are considered, etc. Ugh.
|
||||
-----------------------------------------
|
||||
*/
|
||||
if (attisset)
|
||||
{
|
||||
Type t = type("oid");
|
||||
|
||||
att->attlen = tlen(t);
|
||||
att->attbyval = tbyval(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
att->attlen = typeForm->typlen;
|
||||
att->attbyval = typeForm->typbyval;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TupleDescMakeSelfReference
|
||||
* TupleDescMakeSelfReference
|
||||
*
|
||||
* This function initializes a "self-referential" attribute like
|
||||
* manager in "create EMP (name=text, manager = EMP)".
|
||||
* It calls TypeShellMake() which inserts a "shell" type
|
||||
* tuple into pg_type. A self-reference is one kind of set, so
|
||||
* its size and byval are the same as for a set. See the comments
|
||||
* above in TupleDescInitEntry.
|
||||
* This function initializes a "self-referential" attribute like
|
||||
* manager in "create EMP (name=text, manager = EMP)".
|
||||
* It calls TypeShellMake() which inserts a "shell" type
|
||||
* tuple into pg_type. A self-reference is one kind of set, so
|
||||
* its size and byval are the same as for a set. See the comments
|
||||
* above in TupleDescInitEntry.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static void
|
||||
TupleDescMakeSelfReference(TupleDesc desc,
|
||||
AttrNumber attnum,
|
||||
char *relname)
|
||||
AttrNumber attnum,
|
||||
char *relname)
|
||||
{
|
||||
AttributeTupleForm att;
|
||||
Type t = type("oid");
|
||||
|
||||
att = desc->attrs[attnum-1];
|
||||
att->atttypid = TypeShellMake(relname);
|
||||
att->attlen = tlen(t);
|
||||
att->attbyval = tbyval(t);
|
||||
att->attnelems = 0;
|
||||
AttributeTupleForm att;
|
||||
Type t = type("oid");
|
||||
|
||||
att = desc->attrs[attnum - 1];
|
||||
att->atttypid = TypeShellMake(relname);
|
||||
att->attlen = tlen(t);
|
||||
att->attbyval = tbyval(t);
|
||||
att->attnelems = 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* BuildDescForRelation
|
||||
* BuildDescForRelation
|
||||
*
|
||||
* This is a general purpose function identical to BuildDesc
|
||||
* but is used by the DefineRelation() code to catch the
|
||||
* special case where you
|
||||
* This is a general purpose function identical to BuildDesc
|
||||
* but is used by the DefineRelation() code to catch the
|
||||
* special case where you
|
||||
*
|
||||
* create FOO ( ..., x = FOO )
|
||||
* create FOO ( ..., x = FOO )
|
||||
*
|
||||
* here, the initial type lookup for "x = FOO" will fail
|
||||
* because FOO isn't in the catalogs yet. But since we
|
||||
* are creating FOO, instead of doing an elog() we add
|
||||
* a shell type tuple to pg_type and fix things later
|
||||
* in amcreate().
|
||||
* here, the initial type lookup for "x = FOO" will fail
|
||||
* because FOO isn't in the catalogs yet. But since we
|
||||
* are creating FOO, instead of doing an elog() we add
|
||||
* a shell type tuple to pg_type and fix things later
|
||||
* in amcreate().
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleDesc
|
||||
BuildDescForRelation(List *schema, char *relname)
|
||||
BuildDescForRelation(List * schema, char *relname)
|
||||
{
|
||||
int natts;
|
||||
AttrNumber attnum;
|
||||
List *p;
|
||||
TupleDesc desc;
|
||||
AttrDefault *attrdef = NULL;
|
||||
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
|
||||
char *attname;
|
||||
char *typename;
|
||||
int attdim;
|
||||
int ndef = 0;
|
||||
bool attisset;
|
||||
|
||||
/* ----------------
|
||||
* allocate a new tuple descriptor
|
||||
* ----------------
|
||||
*/
|
||||
natts = length(schema);
|
||||
desc = CreateTemplateTupleDesc(natts);
|
||||
constr->has_not_null = false;
|
||||
|
||||
attnum = 0;
|
||||
|
||||
typename = palloc(NAMEDATALEN);
|
||||
|
||||
foreach(p, schema) {
|
||||
ColumnDef *entry;
|
||||
List *arry;
|
||||
int natts;
|
||||
AttrNumber attnum;
|
||||
List *p;
|
||||
TupleDesc desc;
|
||||
AttrDefault *attrdef = NULL;
|
||||
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
|
||||
char *attname;
|
||||
char *typename;
|
||||
int attdim;
|
||||
int ndef = 0;
|
||||
bool attisset;
|
||||
|
||||
/* ----------------
|
||||
* for each entry in the list, get the name and type
|
||||
* information from the list and have TupleDescInitEntry
|
||||
* fill in the attribute information we need.
|
||||
* allocate a new tuple descriptor
|
||||
* ----------------
|
||||
*/
|
||||
attnum++;
|
||||
|
||||
entry = lfirst(p);
|
||||
attname = entry->colname;
|
||||
arry = entry->typename->arrayBounds;
|
||||
attisset = entry->typename->setof;
|
||||
|
||||
strNcpy(typename, entry->typename->name,NAMEDATALEN-1);
|
||||
if (arry != NIL)
|
||||
attdim = length(arry);
|
||||
else
|
||||
attdim = 0;
|
||||
|
||||
if (! TupleDescInitEntry(desc, attnum, attname,
|
||||
typename, attdim, attisset)) {
|
||||
/* ----------------
|
||||
* if TupleDescInitEntry() fails, it means there is
|
||||
* no type in the system catalogs. So now we check if
|
||||
* the type name equals the relation name. If so we
|
||||
* have a self reference, otherwise it's an error.
|
||||
* ----------------
|
||||
*/
|
||||
if (!strcmp(typename, relname)) {
|
||||
TupleDescMakeSelfReference(desc, attnum, relname);
|
||||
} else
|
||||
elog(WARN, "DefineRelation: no such type %s",
|
||||
typename);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is for char() and varchar(). When an entry is of type
|
||||
* char() or varchar(), typlen is set to the appropriate length,
|
||||
* which we'll use here instead. (The catalog lookup only returns
|
||||
* the length of bpchar and varchar which is not what we want!)
|
||||
* - ay 6/95
|
||||
*/
|
||||
if (entry->typename->typlen > 0) {
|
||||
desc->attrs[attnum - 1]->attlen = entry->typename->typlen;
|
||||
}
|
||||
natts = length(schema);
|
||||
desc = CreateTemplateTupleDesc(natts);
|
||||
constr->has_not_null = false;
|
||||
|
||||
/* This is for constraints */
|
||||
if (entry->is_not_null)
|
||||
constr->has_not_null = true;
|
||||
desc->attrs[attnum-1]->attnotnull = entry->is_not_null;
|
||||
|
||||
if ( entry->defval != NULL )
|
||||
attnum = 0;
|
||||
|
||||
typename = palloc(NAMEDATALEN);
|
||||
|
||||
foreach(p, schema)
|
||||
{
|
||||
if ( attrdef == NULL )
|
||||
attrdef = (AttrDefault*) palloc (natts * sizeof (AttrDefault));
|
||||
attrdef[ndef].adnum = attnum;
|
||||
attrdef[ndef].adbin = NULL;
|
||||
attrdef[ndef].adsrc = entry->defval;
|
||||
ndef++;
|
||||
desc->attrs[attnum-1]->atthasdef = true;
|
||||
ColumnDef *entry;
|
||||
List *arry;
|
||||
|
||||
/* ----------------
|
||||
* for each entry in the list, get the name and type
|
||||
* information from the list and have TupleDescInitEntry
|
||||
* fill in the attribute information we need.
|
||||
* ----------------
|
||||
*/
|
||||
attnum++;
|
||||
|
||||
entry = lfirst(p);
|
||||
attname = entry->colname;
|
||||
arry = entry->typename->arrayBounds;
|
||||
attisset = entry->typename->setof;
|
||||
|
||||
strNcpy(typename, entry->typename->name, NAMEDATALEN - 1);
|
||||
if (arry != NIL)
|
||||
attdim = length(arry);
|
||||
else
|
||||
attdim = 0;
|
||||
|
||||
if (!TupleDescInitEntry(desc, attnum, attname,
|
||||
typename, attdim, attisset))
|
||||
{
|
||||
/* ----------------
|
||||
* if TupleDescInitEntry() fails, it means there is
|
||||
* no type in the system catalogs. So now we check if
|
||||
* the type name equals the relation name. If so we
|
||||
* have a self reference, otherwise it's an error.
|
||||
* ----------------
|
||||
*/
|
||||
if (!strcmp(typename, relname))
|
||||
{
|
||||
TupleDescMakeSelfReference(desc, attnum, relname);
|
||||
}
|
||||
else
|
||||
elog(WARN, "DefineRelation: no such type %s",
|
||||
typename);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is for char() and varchar(). When an entry is of type
|
||||
* char() or varchar(), typlen is set to the appropriate length,
|
||||
* which we'll use here instead. (The catalog lookup only returns
|
||||
* the length of bpchar and varchar which is not what we want!) -
|
||||
* ay 6/95
|
||||
*/
|
||||
if (entry->typename->typlen > 0)
|
||||
{
|
||||
desc->attrs[attnum - 1]->attlen = entry->typename->typlen;
|
||||
}
|
||||
|
||||
/* This is for constraints */
|
||||
if (entry->is_not_null)
|
||||
constr->has_not_null = true;
|
||||
desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
|
||||
|
||||
if (entry->defval != NULL)
|
||||
{
|
||||
if (attrdef == NULL)
|
||||
attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
|
||||
attrdef[ndef].adnum = attnum;
|
||||
attrdef[ndef].adbin = NULL;
|
||||
attrdef[ndef].adsrc = entry->defval;
|
||||
ndef++;
|
||||
desc->attrs[attnum - 1]->atthasdef = true;
|
||||
}
|
||||
|
||||
}
|
||||
if (constr->has_not_null || ndef > 0)
|
||||
{
|
||||
desc->constr = constr;
|
||||
|
||||
}
|
||||
if ( constr->has_not_null || ndef > 0 )
|
||||
{
|
||||
desc->constr = constr;
|
||||
|
||||
if ( ndef > 0 ) /* DEFAULTs */
|
||||
{
|
||||
if ( ndef < natts )
|
||||
constr->defval = (AttrDefault*)
|
||||
repalloc (attrdef, ndef * sizeof (AttrDefault));
|
||||
else
|
||||
constr->defval = attrdef;
|
||||
constr->num_defval = ndef;
|
||||
}
|
||||
else
|
||||
constr->num_defval = 0;
|
||||
constr->num_check = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pfree (constr);
|
||||
desc->constr = NULL;
|
||||
}
|
||||
return desc;
|
||||
if (ndef > 0) /* DEFAULTs */
|
||||
{
|
||||
if (ndef < natts)
|
||||
constr->defval = (AttrDefault *)
|
||||
repalloc(attrdef, ndef * sizeof(AttrDefault));
|
||||
else
|
||||
constr->defval = attrdef;
|
||||
constr->num_defval = ndef;
|
||||
}
|
||||
else
|
||||
constr->num_defval = 0;
|
||||
constr->num_check = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pfree(constr);
|
||||
desc->constr = NULL;
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* gistget.c--
|
||||
* fetch tuples from a GiST scan.
|
||||
* fetch tuples from a GiST scan.
|
||||
*
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* /usr/local/devel/pglite/cvs/src/backend/access/gisr/gistget.c,v 1.9.1 1996/11/21 01:00:00 vadim Exp
|
||||
* /usr/local/devel/pglite/cvs/src/backend/access/gisr/gistget.c,v 1.9.1 1996/11/21 01:00:00 vadim Exp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -22,350 +22,392 @@
|
||||
#include <storage/bufmgr.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
|
||||
static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n,
|
||||
ScanDirection dir);
|
||||
static OffsetNumber
|
||||
gistfindnext(IndexScanDesc s, Page p, OffsetNumber n,
|
||||
ScanDirection dir);
|
||||
static RetrieveIndexResult gistscancache(IndexScanDesc s, ScanDirection dir);
|
||||
static RetrieveIndexResult gistfirst(IndexScanDesc s, ScanDirection dir);
|
||||
static RetrieveIndexResult gistnext(IndexScanDesc s, ScanDirection dir);
|
||||
static ItemPointer gistheapptr(Relation r, ItemPointer itemp);
|
||||
static bool gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc,
|
||||
int scanKeySize, ScanKey key, GISTSTATE *giststate,
|
||||
Relation r, Page p, OffsetNumber offset);
|
||||
static bool
|
||||
gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc,
|
||||
int scanKeySize, ScanKey key, GISTSTATE * giststate,
|
||||
Relation r, Page p, OffsetNumber offset);
|
||||
|
||||
|
||||
RetrieveIndexResult
|
||||
gistgettuple(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
RetrieveIndexResult res;
|
||||
|
||||
/* if we have it cached in the scan desc, just return the value */
|
||||
if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL)
|
||||
RetrieveIndexResult res;
|
||||
|
||||
/* if we have it cached in the scan desc, just return the value */
|
||||
if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL)
|
||||
return (res);
|
||||
|
||||
/* not cached, so we'll have to do some work */
|
||||
if (ItemPointerIsValid(&(s->currentItemData)))
|
||||
{
|
||||
res = gistnext(s, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = gistfirst(s, dir);
|
||||
}
|
||||
return (res);
|
||||
|
||||
/* not cached, so we'll have to do some work */
|
||||
if (ItemPointerIsValid(&(s->currentItemData))) {
|
||||
res = gistnext(s, dir);
|
||||
} else {
|
||||
res = gistfirst(s, dir);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
static RetrieveIndexResult
|
||||
static RetrieveIndexResult
|
||||
gistfirst(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
GISTPageOpaque po;
|
||||
GISTScanOpaque so;
|
||||
GISTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
GISTPageOpaque po;
|
||||
GISTScanOpaque so;
|
||||
GISTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
b = ReadBuffer(s->relation, GISTP_ROOT);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
b = ReadBuffer(s->relation, GISTP_ROOT);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
for (;;) {
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
n = gistfindnext(s, p, maxoff, dir);
|
||||
else
|
||||
n = gistfindnext(s, p, FirstOffsetNumber, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff) {
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (GISTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->gs_blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = OffsetNumberPrev(stk->gs_child);
|
||||
} else {
|
||||
n = OffsetNumberNext(stk->gs_child);
|
||||
}
|
||||
so->s_stack = stk->gs_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = gistfindnext(s, p, n, dir);
|
||||
for (;;)
|
||||
{
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
n = gistfindnext(s, p, maxoff, dir);
|
||||
else
|
||||
n = gistfindnext(s, p, FirstOffsetNumber, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff)
|
||||
{
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (GISTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->gs_blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = OffsetNumberPrev(stk->gs_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberNext(stk->gs_child);
|
||||
}
|
||||
so->s_stack = stk->gs_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = gistfindnext(s, p, n, dir);
|
||||
}
|
||||
if (po->flags & F_LEAF)
|
||||
{
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
}
|
||||
else
|
||||
{
|
||||
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
stk->gs_child = n;
|
||||
stk->gs_blk = BufferGetBlockNumber(b);
|
||||
stk->gs_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
}
|
||||
}
|
||||
if (po->flags & F_LEAF) {
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
} else {
|
||||
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
stk->gs_child = n;
|
||||
stk->gs_blk = BufferGetBlockNumber(b);
|
||||
stk->gs_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RetrieveIndexResult
|
||||
static RetrieveIndexResult
|
||||
gistnext(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
GISTPageOpaque po;
|
||||
GISTScanOpaque so;
|
||||
GISTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
|
||||
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
|
||||
|
||||
if (ScanDirectionIsForward(dir)) {
|
||||
n = OffsetNumberNext(n);
|
||||
} else {
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
GISTPageOpaque po;
|
||||
GISTScanOpaque so;
|
||||
GISTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
for (;;) {
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
n = gistfindnext(s, p, n, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff) {
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (GISTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->gs_blk);
|
||||
p = BufferGetPage(b);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = OffsetNumberPrev(stk->gs_child);
|
||||
} else {
|
||||
n = OffsetNumberNext(stk->gs_child);
|
||||
}
|
||||
so->s_stack = stk->gs_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = gistfindnext(s, p, n, dir);
|
||||
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
|
||||
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
|
||||
|
||||
if (ScanDirectionIsForward(dir))
|
||||
{
|
||||
n = OffsetNumberNext(n);
|
||||
}
|
||||
if (po->flags & F_LEAF) {
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
} else {
|
||||
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
stk->gs_child = n;
|
||||
stk->gs_blk = BufferGetBlockNumber(b);
|
||||
stk->gs_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = PageGetMaxOffsetNumber(p);
|
||||
} else {
|
||||
n = FirstOffsetNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
n = gistfindnext(s, p, n, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff)
|
||||
{
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (GISTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->gs_blk);
|
||||
p = BufferGetPage(b);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = OffsetNumberPrev(stk->gs_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberNext(stk->gs_child);
|
||||
}
|
||||
so->s_stack = stk->gs_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = gistfindnext(s, p, n, dir);
|
||||
}
|
||||
if (po->flags & F_LEAF)
|
||||
{
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
}
|
||||
else
|
||||
{
|
||||
stk = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
stk->gs_child = n;
|
||||
stk->gs_blk = BufferGetBlockNumber(b);
|
||||
stk->gs_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = PageGetMaxOffsetNumber(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = FirstOffsetNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Similar to index_keytest, but decompresses the key in the IndexTuple */
|
||||
static bool
|
||||
static bool
|
||||
gistindex_keytest(IndexTuple tuple,
|
||||
TupleDesc tupdesc,
|
||||
int scanKeySize,
|
||||
ScanKey key,
|
||||
GISTSTATE *giststate,
|
||||
Relation r,
|
||||
Page p,
|
||||
OffsetNumber offset)
|
||||
TupleDesc tupdesc,
|
||||
int scanKeySize,
|
||||
ScanKey key,
|
||||
GISTSTATE * giststate,
|
||||
Relation r,
|
||||
Page p,
|
||||
OffsetNumber offset)
|
||||
{
|
||||
bool isNull;
|
||||
Datum datum;
|
||||
int test;
|
||||
GISTENTRY de;
|
||||
bool isNull;
|
||||
Datum datum;
|
||||
int test;
|
||||
GISTENTRY de;
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
while (scanKeySize > 0) {
|
||||
datum = index_getattr(tuple,
|
||||
1,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
gistdentryinit(giststate, &de, (char *)datum, r, p, offset,
|
||||
IndexTupleSize(tuple) - sizeof(IndexTupleData),
|
||||
FALSE);
|
||||
|
||||
if (isNull) {
|
||||
/* XXX eventually should check if SK_ISNULL */
|
||||
return (false);
|
||||
|
||||
while (scanKeySize > 0)
|
||||
{
|
||||
datum = index_getattr(tuple,
|
||||
1,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
gistdentryinit(giststate, &de, (char *) datum, r, p, offset,
|
||||
IndexTupleSize(tuple) - sizeof(IndexTupleData),
|
||||
FALSE);
|
||||
|
||||
if (isNull)
|
||||
{
|
||||
/* XXX eventually should check if SK_ISNULL */
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE)
|
||||
{
|
||||
test = (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
&de, key[0].sk_procedure) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
test = (*(key[0].sk_func))
|
||||
(&de,
|
||||
DatumGetPointer(key[0].sk_argument),
|
||||
key[0].sk_procedure) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
scanKeySize -= 1;
|
||||
key++;
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE) {
|
||||
test = (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
&de, key[0].sk_procedure) ? 1 : 0;
|
||||
} else {
|
||||
test = (*(key[0].sk_func))
|
||||
(&de,
|
||||
DatumGetPointer(key[0].sk_argument),
|
||||
key[0].sk_procedure) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
scanKeySize -= 1;
|
||||
key++;
|
||||
}
|
||||
|
||||
return (true);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
static OffsetNumber
|
||||
static OffsetNumber
|
||||
gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
|
||||
{
|
||||
OffsetNumber maxoff;
|
||||
char *it;
|
||||
GISTPageOpaque po;
|
||||
GISTScanOpaque so;
|
||||
GISTSTATE *giststate;
|
||||
OffsetNumber maxoff;
|
||||
char *it;
|
||||
GISTPageOpaque po;
|
||||
GISTScanOpaque so;
|
||||
GISTSTATE *giststate;
|
||||
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
giststate = so->giststate;
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (GISTPageOpaque) PageGetSpecialPointer(p);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
giststate = so->giststate;
|
||||
|
||||
/*
|
||||
* If we modified the index during the scan, we may have a pointer to
|
||||
* a ghost tuple, before the scan. If this is the case, back up one.
|
||||
*/
|
||||
|
||||
if (so->s_flags & GS_CURBEFORE) {
|
||||
so->s_flags &= ~GS_CURBEFORE;
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
|
||||
while (n >= FirstOffsetNumber && n <= maxoff) {
|
||||
it = (char *) PageGetItem(p, PageGetItemId(p, n));
|
||||
if (gistindex_keytest((IndexTuple) it,
|
||||
RelationGetTupleDescriptor(s->relation),
|
||||
s->numberOfKeys, s->keyData, giststate,
|
||||
s->relation, p, n))
|
||||
break;
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = OffsetNumberPrev(n);
|
||||
} else {
|
||||
n = OffsetNumberNext(n);
|
||||
/*
|
||||
* If we modified the index during the scan, we may have a pointer to
|
||||
* a ghost tuple, before the scan. If this is the case, back up one.
|
||||
*/
|
||||
|
||||
if (so->s_flags & GS_CURBEFORE)
|
||||
{
|
||||
so->s_flags &= ~GS_CURBEFORE;
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
}
|
||||
|
||||
return (n);
|
||||
|
||||
while (n >= FirstOffsetNumber && n <= maxoff)
|
||||
{
|
||||
it = (char *) PageGetItem(p, PageGetItemId(p, n));
|
||||
if (gistindex_keytest((IndexTuple) it,
|
||||
RelationGetTupleDescriptor(s->relation),
|
||||
s->numberOfKeys, s->keyData, giststate,
|
||||
s->relation, p, n))
|
||||
break;
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberNext(n);
|
||||
}
|
||||
}
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
static RetrieveIndexResult
|
||||
static RetrieveIndexResult
|
||||
gistscancache(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
RetrieveIndexResult res;
|
||||
ItemPointer ip;
|
||||
|
||||
if (!(ScanDirectionIsNoMovement(dir)
|
||||
&& ItemPointerIsValid(&(s->currentItemData)))) {
|
||||
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
|
||||
ip = gistheapptr(s->relation, &(s->currentItemData));
|
||||
|
||||
if (ItemPointerIsValid(ip))
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
|
||||
else
|
||||
res = (RetrieveIndexResult) NULL;
|
||||
RetrieveIndexResult res;
|
||||
ItemPointer ip;
|
||||
|
||||
pfree (ip);
|
||||
|
||||
return (res);
|
||||
if (!(ScanDirectionIsNoMovement(dir)
|
||||
&& ItemPointerIsValid(&(s->currentItemData))))
|
||||
{
|
||||
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
|
||||
ip = gistheapptr(s->relation, &(s->currentItemData));
|
||||
|
||||
if (ItemPointerIsValid(ip))
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
|
||||
else
|
||||
res = (RetrieveIndexResult) NULL;
|
||||
|
||||
pfree(ip);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* gistheapptr returns the item pointer to the tuple in the heap relation
|
||||
* for which itemp is the index relation item pointer.
|
||||
* gistheapptr returns the item pointer to the tuple in the heap relation
|
||||
* for which itemp is the index relation item pointer.
|
||||
*/
|
||||
static ItemPointer
|
||||
static ItemPointer
|
||||
gistheapptr(Relation r, ItemPointer itemp)
|
||||
{
|
||||
Buffer b;
|
||||
Page p;
|
||||
IndexTuple it;
|
||||
ItemPointer ip;
|
||||
OffsetNumber n;
|
||||
|
||||
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
|
||||
if (ItemPointerIsValid(itemp)) {
|
||||
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
|
||||
p = BufferGetPage(b);
|
||||
n = ItemPointerGetOffsetNumber(itemp);
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
memmove((char *) ip, (char *) &(it->t_tid),
|
||||
sizeof(ItemPointerData));
|
||||
ReleaseBuffer(b);
|
||||
} else {
|
||||
ItemPointerSetInvalid(ip);
|
||||
}
|
||||
|
||||
return (ip);
|
||||
Buffer b;
|
||||
Page p;
|
||||
IndexTuple it;
|
||||
ItemPointer ip;
|
||||
OffsetNumber n;
|
||||
|
||||
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
|
||||
if (ItemPointerIsValid(itemp))
|
||||
{
|
||||
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
|
||||
p = BufferGetPage(b);
|
||||
n = ItemPointerGetOffsetNumber(itemp);
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
memmove((char *) ip, (char *) &(it->t_tid),
|
||||
sizeof(ItemPointerData));
|
||||
ReleaseBuffer(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPointerSetInvalid(ip);
|
||||
}
|
||||
|
||||
return (ip);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* gistscan.c--
|
||||
* routines to manage scans on index relations
|
||||
* routines to manage scans on index relations
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp
|
||||
* /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -18,375 +18,411 @@
|
||||
#include <access/rtree.h>
|
||||
#include <storage/bufmgr.h>
|
||||
#include <access/giststrat.h>
|
||||
#include <storage/lmgr.h>
|
||||
#include <storage/lmgr.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/* routines defined and used here */
|
||||
static void gistregscan(IndexScanDesc s);
|
||||
static void gistdropscan(IndexScanDesc s);
|
||||
static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
|
||||
OffsetNumber offnum);
|
||||
static void adjuststack(GISTSTACK *stk, BlockNumber blkno,
|
||||
static void gistregscan(IndexScanDesc s);
|
||||
static void gistdropscan(IndexScanDesc s);
|
||||
static void
|
||||
gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
|
||||
OffsetNumber offnum);
|
||||
static void
|
||||
adjuststack(GISTSTACK * stk, BlockNumber blkno,
|
||||
OffsetNumber offnum);
|
||||
static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
|
||||
int op, BlockNumber blkno, OffsetNumber offnum);
|
||||
static void
|
||||
adjustiptr(IndexScanDesc s, ItemPointer iptr,
|
||||
int op, BlockNumber blkno, OffsetNumber offnum);
|
||||
|
||||
/*
|
||||
* Whenever we start a GiST scan in a backend, we register it in private
|
||||
* space. Then if the GiST index gets updated, we check all registered
|
||||
* scans and adjust them if the tuple they point at got moved by the
|
||||
* update. We only need to do this in private space, because when we update
|
||||
* an GiST we have a write lock on the tree, so no other process can have
|
||||
* any locks at all on it. A single transaction can have write and read
|
||||
* locks on the same object, so that's why we need to handle this case.
|
||||
* Whenever we start a GiST scan in a backend, we register it in private
|
||||
* space. Then if the GiST index gets updated, we check all registered
|
||||
* scans and adjust them if the tuple they point at got moved by the
|
||||
* update. We only need to do this in private space, because when we update
|
||||
* an GiST we have a write lock on the tree, so no other process can have
|
||||
* any locks at all on it. A single transaction can have write and read
|
||||
* locks on the same object, so that's why we need to handle this case.
|
||||
*/
|
||||
|
||||
typedef struct GISTScanListData {
|
||||
IndexScanDesc gsl_scan;
|
||||
struct GISTScanListData *gsl_next;
|
||||
} GISTScanListData;
|
||||
typedef struct GISTScanListData
|
||||
{
|
||||
IndexScanDesc gsl_scan;
|
||||
struct GISTScanListData *gsl_next;
|
||||
} GISTScanListData;
|
||||
|
||||
typedef GISTScanListData *GISTScanList;
|
||||
typedef GISTScanListData *GISTScanList;
|
||||
|
||||
/* pointer to list of local scans on GiSTs */
|
||||
static GISTScanList GISTScans = (GISTScanList) NULL;
|
||||
|
||||
|
||||
IndexScanDesc
|
||||
gistbeginscan(Relation r,
|
||||
bool fromEnd,
|
||||
uint16 nkeys,
|
||||
ScanKey key)
|
||||
bool fromEnd,
|
||||
uint16 nkeys,
|
||||
ScanKey key)
|
||||
{
|
||||
IndexScanDesc s;
|
||||
|
||||
RelationSetLockForRead(r);
|
||||
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
|
||||
gistregscan(s);
|
||||
|
||||
return (s);
|
||||
IndexScanDesc s;
|
||||
|
||||
RelationSetLockForRead(r);
|
||||
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
|
||||
gistregscan(s);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
void
|
||||
gistrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
|
||||
{
|
||||
GISTScanOpaque p;
|
||||
int i;
|
||||
|
||||
if (!IndexScanIsValid(s)) {
|
||||
elog(WARN, "gistrescan: invalid scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all the pointers.
|
||||
*/
|
||||
|
||||
ItemPointerSetInvalid(&s->previousItemData);
|
||||
ItemPointerSetInvalid(&s->currentItemData);
|
||||
ItemPointerSetInvalid(&s->nextItemData);
|
||||
ItemPointerSetInvalid(&s->previousMarkData);
|
||||
ItemPointerSetInvalid(&s->currentMarkData);
|
||||
ItemPointerSetInvalid(&s->nextMarkData);
|
||||
|
||||
/*
|
||||
* Set flags.
|
||||
*/
|
||||
if (RelationGetNumberOfBlocks(s->relation) == 0) {
|
||||
s->flags = ScanUnmarked;
|
||||
} else if (fromEnd) {
|
||||
s->flags = ScanUnmarked | ScanUncheckedPrevious;
|
||||
} else {
|
||||
s->flags = ScanUnmarked | ScanUncheckedNext;
|
||||
}
|
||||
|
||||
s->scanFromEnd = fromEnd;
|
||||
|
||||
if (s->numberOfKeys > 0) {
|
||||
memmove(s->keyData,
|
||||
key,
|
||||
s->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
if (p != (GISTScanOpaque) NULL) {
|
||||
gistfreestack(p->s_stack);
|
||||
gistfreestack(p->s_markstk);
|
||||
p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
|
||||
p->s_flags = 0x0;
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
GISTScanOpaque p;
|
||||
int i;
|
||||
|
||||
if (!IndexScanIsValid(s))
|
||||
{
|
||||
s->keyData[i].sk_procedure
|
||||
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
s->keyData[i].sk_func = p->giststate->consistentFn;
|
||||
elog(WARN, "gistrescan: invalid scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all the pointers.
|
||||
*/
|
||||
|
||||
ItemPointerSetInvalid(&s->previousItemData);
|
||||
ItemPointerSetInvalid(&s->currentItemData);
|
||||
ItemPointerSetInvalid(&s->nextItemData);
|
||||
ItemPointerSetInvalid(&s->previousMarkData);
|
||||
ItemPointerSetInvalid(&s->currentMarkData);
|
||||
ItemPointerSetInvalid(&s->nextMarkData);
|
||||
|
||||
/*
|
||||
* Set flags.
|
||||
*/
|
||||
if (RelationGetNumberOfBlocks(s->relation) == 0)
|
||||
{
|
||||
s->flags = ScanUnmarked;
|
||||
}
|
||||
else if (fromEnd)
|
||||
{
|
||||
s->flags = ScanUnmarked | ScanUncheckedPrevious;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->flags = ScanUnmarked | ScanUncheckedNext;
|
||||
}
|
||||
|
||||
s->scanFromEnd = fromEnd;
|
||||
|
||||
if (s->numberOfKeys > 0)
|
||||
{
|
||||
memmove(s->keyData,
|
||||
key,
|
||||
s->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
if (p != (GISTScanOpaque) NULL)
|
||||
{
|
||||
gistfreestack(p->s_stack);
|
||||
gistfreestack(p->s_markstk);
|
||||
p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
|
||||
p->s_flags = 0x0;
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
{
|
||||
s->keyData[i].sk_procedure
|
||||
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
s->keyData[i].sk_func = p->giststate->consistentFn;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize opaque data */
|
||||
p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
|
||||
p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
|
||||
p->s_flags = 0x0;
|
||||
s->opaque = p;
|
||||
p->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
|
||||
initGISTstate(p->giststate, s->relation);
|
||||
if (s->numberOfKeys > 0)
|
||||
|
||||
/*
|
||||
* * Play games here with the scan key to use the Consistent *
|
||||
* function for all comparisons: * 1) the sk_procedure field
|
||||
* will now be used to hold the * strategy number * 2) the
|
||||
* sk_func field will point to the Consistent function
|
||||
*/
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
{
|
||||
|
||||
/*
|
||||
* s->keyData[i].sk_procedure =
|
||||
* index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC);
|
||||
*/
|
||||
s->keyData[i].sk_procedure
|
||||
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
s->keyData[i].sk_func = p->giststate->consistentFn;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* initialize opaque data */
|
||||
p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
|
||||
p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
|
||||
p->s_flags = 0x0;
|
||||
s->opaque = p;
|
||||
p->giststate = (GISTSTATE *)palloc(sizeof(GISTSTATE));
|
||||
initGISTstate(p->giststate, s->relation);
|
||||
if (s->numberOfKeys > 0)
|
||||
/*
|
||||
** Play games here with the scan key to use the Consistent
|
||||
** function for all comparisons:
|
||||
** 1) the sk_procedure field will now be used to hold the
|
||||
** strategy number
|
||||
** 2) the sk_func field will point to the Consistent function
|
||||
*/
|
||||
for (i = 0; i < s->numberOfKeys; i++) {
|
||||
/* s->keyData[i].sk_procedure
|
||||
= index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC); */
|
||||
s->keyData[i].sk_procedure
|
||||
= RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
s->keyData[i].sk_func = p->giststate->consistentFn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gistmarkpos(IndexScanDesc s)
|
||||
{
|
||||
GISTScanOpaque p;
|
||||
GISTSTACK *o, *n, *tmp;
|
||||
|
||||
s->currentMarkData = s->currentItemData;
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
if (p->s_flags & GS_CURBEFORE)
|
||||
p->s_flags |= GS_MRKBEFORE;
|
||||
else
|
||||
p->s_flags &= ~GS_MRKBEFORE;
|
||||
|
||||
o = (GISTSTACK *) NULL;
|
||||
n = p->s_stack;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (GISTSTACK *) NULL) {
|
||||
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
tmp->gs_child = n->gs_child;
|
||||
tmp->gs_blk = n->gs_blk;
|
||||
tmp->gs_parent = o;
|
||||
o = tmp;
|
||||
n = n->gs_parent;
|
||||
}
|
||||
|
||||
gistfreestack(p->s_markstk);
|
||||
p->s_markstk = o;
|
||||
GISTScanOpaque p;
|
||||
GISTSTACK *o,
|
||||
*n,
|
||||
*tmp;
|
||||
|
||||
s->currentMarkData = s->currentItemData;
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
if (p->s_flags & GS_CURBEFORE)
|
||||
p->s_flags |= GS_MRKBEFORE;
|
||||
else
|
||||
p->s_flags &= ~GS_MRKBEFORE;
|
||||
|
||||
o = (GISTSTACK *) NULL;
|
||||
n = p->s_stack;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (GISTSTACK *) NULL)
|
||||
{
|
||||
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
tmp->gs_child = n->gs_child;
|
||||
tmp->gs_blk = n->gs_blk;
|
||||
tmp->gs_parent = o;
|
||||
o = tmp;
|
||||
n = n->gs_parent;
|
||||
}
|
||||
|
||||
gistfreestack(p->s_markstk);
|
||||
p->s_markstk = o;
|
||||
}
|
||||
|
||||
void
|
||||
gistrestrpos(IndexScanDesc s)
|
||||
{
|
||||
GISTScanOpaque p;
|
||||
GISTSTACK *o, *n, *tmp;
|
||||
|
||||
s->currentItemData = s->currentMarkData;
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
if (p->s_flags & GS_MRKBEFORE)
|
||||
p->s_flags |= GS_CURBEFORE;
|
||||
else
|
||||
p->s_flags &= ~GS_CURBEFORE;
|
||||
|
||||
o = (GISTSTACK *) NULL;
|
||||
n = p->s_markstk;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (GISTSTACK *) NULL) {
|
||||
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
tmp->gs_child = n->gs_child;
|
||||
tmp->gs_blk = n->gs_blk;
|
||||
tmp->gs_parent = o;
|
||||
o = tmp;
|
||||
n = n->gs_parent;
|
||||
}
|
||||
|
||||
gistfreestack(p->s_stack);
|
||||
p->s_stack = o;
|
||||
GISTScanOpaque p;
|
||||
GISTSTACK *o,
|
||||
*n,
|
||||
*tmp;
|
||||
|
||||
s->currentItemData = s->currentMarkData;
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
if (p->s_flags & GS_MRKBEFORE)
|
||||
p->s_flags |= GS_CURBEFORE;
|
||||
else
|
||||
p->s_flags &= ~GS_CURBEFORE;
|
||||
|
||||
o = (GISTSTACK *) NULL;
|
||||
n = p->s_markstk;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (GISTSTACK *) NULL)
|
||||
{
|
||||
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
|
||||
tmp->gs_child = n->gs_child;
|
||||
tmp->gs_blk = n->gs_blk;
|
||||
tmp->gs_parent = o;
|
||||
o = tmp;
|
||||
n = n->gs_parent;
|
||||
}
|
||||
|
||||
gistfreestack(p->s_stack);
|
||||
p->s_stack = o;
|
||||
}
|
||||
|
||||
void
|
||||
gistendscan(IndexScanDesc s)
|
||||
{
|
||||
GISTScanOpaque p;
|
||||
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
|
||||
if (p != (GISTScanOpaque) NULL) {
|
||||
gistfreestack(p->s_stack);
|
||||
gistfreestack(p->s_markstk);
|
||||
pfree (s->opaque);
|
||||
}
|
||||
|
||||
gistdropscan(s);
|
||||
/* XXX don't unset read lock -- two-phase locking */
|
||||
GISTScanOpaque p;
|
||||
|
||||
p = (GISTScanOpaque) s->opaque;
|
||||
|
||||
if (p != (GISTScanOpaque) NULL)
|
||||
{
|
||||
gistfreestack(p->s_stack);
|
||||
gistfreestack(p->s_markstk);
|
||||
pfree(s->opaque);
|
||||
}
|
||||
|
||||
gistdropscan(s);
|
||||
/* XXX don't unset read lock -- two-phase locking */
|
||||
}
|
||||
|
||||
static void
|
||||
gistregscan(IndexScanDesc s)
|
||||
{
|
||||
GISTScanList l;
|
||||
|
||||
l = (GISTScanList) palloc(sizeof(GISTScanListData));
|
||||
l->gsl_scan = s;
|
||||
l->gsl_next = GISTScans;
|
||||
GISTScans = l;
|
||||
GISTScanList l;
|
||||
|
||||
l = (GISTScanList) palloc(sizeof(GISTScanListData));
|
||||
l->gsl_scan = s;
|
||||
l->gsl_next = GISTScans;
|
||||
GISTScans = l;
|
||||
}
|
||||
|
||||
static void
|
||||
gistdropscan(IndexScanDesc s)
|
||||
{
|
||||
GISTScanList l;
|
||||
GISTScanList prev;
|
||||
|
||||
prev = (GISTScanList) NULL;
|
||||
|
||||
for (l = GISTScans;
|
||||
l != (GISTScanList) NULL && l->gsl_scan != s;
|
||||
l = l->gsl_next) {
|
||||
prev = l;
|
||||
}
|
||||
|
||||
if (l == (GISTScanList) NULL)
|
||||
elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s);
|
||||
|
||||
if (prev == (GISTScanList) NULL)
|
||||
GISTScans = l->gsl_next;
|
||||
else
|
||||
prev->gsl_next = l->gsl_next;
|
||||
|
||||
pfree(l);
|
||||
GISTScanList l;
|
||||
GISTScanList prev;
|
||||
|
||||
prev = (GISTScanList) NULL;
|
||||
|
||||
for (l = GISTScans;
|
||||
l != (GISTScanList) NULL && l->gsl_scan != s;
|
||||
l = l->gsl_next)
|
||||
{
|
||||
prev = l;
|
||||
}
|
||||
|
||||
if (l == (GISTScanList) NULL)
|
||||
elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s);
|
||||
|
||||
if (prev == (GISTScanList) NULL)
|
||||
GISTScans = l->gsl_next;
|
||||
else
|
||||
prev->gsl_next = l->gsl_next;
|
||||
|
||||
pfree(l);
|
||||
}
|
||||
|
||||
void
|
||||
gistadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
|
||||
{
|
||||
GISTScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = r->rd_id;
|
||||
for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next) {
|
||||
if (l->gsl_scan->relation->rd_id == relid)
|
||||
gistadjone(l->gsl_scan, op, blkno, offnum);
|
||||
}
|
||||
GISTScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = r->rd_id;
|
||||
for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next)
|
||||
{
|
||||
if (l->gsl_scan->relation->rd_id == relid)
|
||||
gistadjone(l->gsl_scan, op, blkno, offnum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gistadjone() -- adjust one scan for update.
|
||||
* gistadjone() -- adjust one scan for update.
|
||||
*
|
||||
* By here, the scan passed in is on a modified relation. Op tells
|
||||
* us what the modification is, and blkno and offind tell us what
|
||||
* block and offset index were affected. This routine checks the
|
||||
* current and marked positions, and the current and marked stacks,
|
||||
* to see if any stored location needs to be changed because of the
|
||||
* update. If so, we make the change here.
|
||||
* By here, the scan passed in is on a modified relation. Op tells
|
||||
* us what the modification is, and blkno and offind tell us what
|
||||
* block and offset index were affected. This routine checks the
|
||||
* current and marked positions, and the current and marked stacks,
|
||||
* to see if any stored location needs to be changed because of the
|
||||
* update. If so, we make the change here.
|
||||
*/
|
||||
static void
|
||||
gistadjone(IndexScanDesc s,
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
{
|
||||
GISTScanOpaque so;
|
||||
|
||||
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
|
||||
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
|
||||
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
if (op == GISTOP_SPLIT) {
|
||||
adjuststack(so->s_stack, blkno, offnum);
|
||||
adjuststack(so->s_markstk, blkno, offnum);
|
||||
}
|
||||
GISTScanOpaque so;
|
||||
|
||||
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
|
||||
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
|
||||
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
if (op == GISTOP_SPLIT)
|
||||
{
|
||||
adjuststack(so->s_stack, blkno, offnum);
|
||||
adjuststack(so->s_markstk, blkno, offnum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adjustiptr() -- adjust current and marked item pointers in the scan
|
||||
* adjustiptr() -- adjust current and marked item pointers in the scan
|
||||
*
|
||||
* Depending on the type of update and the place it happened, we
|
||||
* need to do nothing, to back up one record, or to start over on
|
||||
* the same page.
|
||||
* Depending on the type of update and the place it happened, we
|
||||
* need to do nothing, to back up one record, or to start over on
|
||||
* the same page.
|
||||
*/
|
||||
static void
|
||||
adjustiptr(IndexScanDesc s,
|
||||
ItemPointer iptr,
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
ItemPointer iptr,
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
{
|
||||
OffsetNumber curoff;
|
||||
GISTScanOpaque so;
|
||||
|
||||
if (ItemPointerIsValid(iptr)) {
|
||||
if (ItemPointerGetBlockNumber(iptr) == blkno) {
|
||||
curoff = ItemPointerGetOffsetNumber(iptr);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
switch (op) {
|
||||
case GISTOP_DEL:
|
||||
/* back up one if we need to */
|
||||
if (curoff >= offnum) {
|
||||
|
||||
if (curoff > FirstOffsetNumber) {
|
||||
/* just adjust the item pointer */
|
||||
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
|
||||
} else {
|
||||
/* remember that we're before the current tuple */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags |= GS_CURBEFORE;
|
||||
else
|
||||
so->s_flags |= GS_MRKBEFORE;
|
||||
}
|
||||
OffsetNumber curoff;
|
||||
GISTScanOpaque so;
|
||||
|
||||
if (ItemPointerIsValid(iptr))
|
||||
{
|
||||
if (ItemPointerGetBlockNumber(iptr) == blkno)
|
||||
{
|
||||
curoff = ItemPointerGetOffsetNumber(iptr);
|
||||
so = (GISTScanOpaque) s->opaque;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GISTOP_DEL:
|
||||
/* back up one if we need to */
|
||||
if (curoff >= offnum)
|
||||
{
|
||||
|
||||
if (curoff > FirstOffsetNumber)
|
||||
{
|
||||
/* just adjust the item pointer */
|
||||
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* remember that we're before the current tuple */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags |= GS_CURBEFORE;
|
||||
else
|
||||
so->s_flags |= GS_MRKBEFORE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GISTOP_SPLIT:
|
||||
/* back to start of page on split */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags &= ~GS_CURBEFORE;
|
||||
else
|
||||
so->s_flags &= ~GS_MRKBEFORE;
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "Bad operation in GiST scan adjust: %d", op);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GISTOP_SPLIT:
|
||||
/* back to start of page on split */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags &= ~GS_CURBEFORE;
|
||||
else
|
||||
so->s_flags &= ~GS_MRKBEFORE;
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "Bad operation in GiST scan adjust: %d", op);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adjuststack() -- adjust the supplied stack for a split on a page in
|
||||
* the index we're scanning.
|
||||
* adjuststack() -- adjust the supplied stack for a split on a page in
|
||||
* the index we're scanning.
|
||||
*
|
||||
* If a page on our parent stack has split, we need to back up to the
|
||||
* beginning of the page and rescan it. The reason for this is that
|
||||
* the split algorithm for GiSTs doesn't order tuples in any useful
|
||||
* way on a single page. This means on that a split, we may wind up
|
||||
* looking at some heap tuples more than once. This is handled in the
|
||||
* access method update code for heaps; if we've modified the tuple we
|
||||
* are looking at already in this transaction, we ignore the update
|
||||
* request.
|
||||
* If a page on our parent stack has split, we need to back up to the
|
||||
* beginning of the page and rescan it. The reason for this is that
|
||||
* the split algorithm for GiSTs doesn't order tuples in any useful
|
||||
* way on a single page. This means on that a split, we may wind up
|
||||
* looking at some heap tuples more than once. This is handled in the
|
||||
* access method update code for heaps; if we've modified the tuple we
|
||||
* are looking at already in this transaction, we ignore the update
|
||||
* request.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
adjuststack(GISTSTACK *stk,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
adjuststack(GISTSTACK * stk,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
{
|
||||
while (stk != (GISTSTACK *) NULL) {
|
||||
if (stk->gs_blk == blkno)
|
||||
stk->gs_child = FirstOffsetNumber;
|
||||
|
||||
stk = stk->gs_parent;
|
||||
}
|
||||
while (stk != (GISTSTACK *) NULL)
|
||||
{
|
||||
if (stk->gs_blk == blkno)
|
||||
stk->gs_child = FirstOffsetNumber;
|
||||
|
||||
stk = stk->gs_parent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,116 +1,117 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* giststrat.c--
|
||||
* strategy map data for GiSTs.
|
||||
* strategy map data for GiSTs.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* /usr/local/devel/pglite/cvs/src/backend/access/gist/giststrat.c,v 1.4 1995/06/14 00:10:05 jolly Exp
|
||||
* /usr/local/devel/pglite/cvs/src/backend/access/gist/giststrat.c,v 1.4 1995/06/14 00:10:05 jolly Exp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <access/gist.h>
|
||||
#include <access/istrat.h>
|
||||
|
||||
/*
|
||||
* Note: negate, commute, and negatecommute all assume that operators are
|
||||
* ordered as follows in the strategy map:
|
||||
* Note: negate, commute, and negatecommute all assume that operators are
|
||||
* ordered as follows in the strategy map:
|
||||
*
|
||||
* contains, contained-by
|
||||
* contains, contained-by
|
||||
*
|
||||
* The negate, commute, and negatecommute arrays are used by the planner
|
||||
* to plan indexed scans over data that appears in the qualificiation in
|
||||
* a boolean negation, or whose operands appear in the wrong order. For
|
||||
* example, if the operator "<%" means "contains", and the user says
|
||||
* The negate, commute, and negatecommute arrays are used by the planner
|
||||
* to plan indexed scans over data that appears in the qualificiation in
|
||||
* a boolean negation, or whose operands appear in the wrong order. For
|
||||
* example, if the operator "<%" means "contains", and the user says
|
||||
*
|
||||
* where not rel.box <% "(10,10,20,20)"::box
|
||||
* where not rel.box <% "(10,10,20,20)"::box
|
||||
*
|
||||
* the planner can plan an index scan by noting that GiST indices have
|
||||
* an operator in their operator class for negating <%.
|
||||
* the planner can plan an index scan by noting that GiST indices have
|
||||
* an operator in their operator class for negating <%.
|
||||
*
|
||||
* Similarly, if the user says something like
|
||||
* Similarly, if the user says something like
|
||||
*
|
||||
* where "(10,10,20,20)"::box <% rel.box
|
||||
* where "(10,10,20,20)"::box <% rel.box
|
||||
*
|
||||
* the planner can see that the GiST index on rel.box has an operator in
|
||||
* its opclass for commuting <%, and plan the scan using that operator.
|
||||
* This added complexity in the access methods makes the planner a lot easier
|
||||
* to write.
|
||||
* the planner can see that the GiST index on rel.box has an operator in
|
||||
* its opclass for commuting <%, and plan the scan using that operator.
|
||||
* This added complexity in the access methods makes the planner a lot easier
|
||||
* to write.
|
||||
*/
|
||||
|
||||
/* if a op b, what operator tells us if (not a op b)? */
|
||||
static StrategyNumber GISTNegate[GISTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
static StrategyNumber GISTNegate[GISTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/* if a op_1 b, what is the operator op_2 such that b op_2 a? */
|
||||
static StrategyNumber GISTCommute[GISTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
static StrategyNumber GISTCommute[GISTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
|
||||
static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/*
|
||||
* GiSTs do not currently support TermData (see rtree/rtstrat.c for
|
||||
* GiSTs do not currently support TermData (see rtree/rtstrat.c for
|
||||
* discussion of
|
||||
* TermData) -- such logic must be encoded in the user's Consistent function.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If you were sufficiently attentive to detail, you would go through
|
||||
* the ExpressionData pain above for every one of the strategies
|
||||
* we defined. I am not. Now we declare the StrategyEvaluationData
|
||||
* structure that gets shipped around to help the planner and the access
|
||||
* method decide what sort of scan it should do, based on (a) what the
|
||||
* user asked for, (b) what operators are defined for a particular opclass,
|
||||
* and (c) the reams of information we supplied above.
|
||||
* If you were sufficiently attentive to detail, you would go through
|
||||
* the ExpressionData pain above for every one of the strategies
|
||||
* we defined. I am not. Now we declare the StrategyEvaluationData
|
||||
* structure that gets shipped around to help the planner and the access
|
||||
* method decide what sort of scan it should do, based on (a) what the
|
||||
* user asked for, (b) what operators are defined for a particular opclass,
|
||||
* and (c) the reams of information we supplied above.
|
||||
*
|
||||
* The idea of all of this initialized data is to make life easier on the
|
||||
* user when he defines a new operator class to use this access method.
|
||||
* By filling in all the data, we let him get away with leaving holes in his
|
||||
* operator class, and still let him use the index. The added complexity
|
||||
* in the access methods just isn't worth the trouble, though.
|
||||
* The idea of all of this initialized data is to make life easier on the
|
||||
* user when he defines a new operator class to use this access method.
|
||||
* By filling in all the data, we let him get away with leaving holes in his
|
||||
* operator class, and still let him use the index. The added complexity
|
||||
* in the access methods just isn't worth the trouble, though.
|
||||
*/
|
||||
|
||||
static StrategyEvaluationData GISTEvaluationData = {
|
||||
GISTNStrategies, /* # of strategies */
|
||||
(StrategyTransformMap) GISTNegate, /* how to do (not qual) */
|
||||
(StrategyTransformMap) GISTCommute, /* how to swap operands */
|
||||
(StrategyTransformMap) GISTNegateCommute, /* how to do both */
|
||||
{ NULL }
|
||||
GISTNStrategies, /* # of strategies */
|
||||
(StrategyTransformMap) GISTNegate, /* how to do (not qual) */
|
||||
(StrategyTransformMap) GISTCommute, /* how to swap operands */
|
||||
(StrategyTransformMap) GISTNegateCommute, /* how to do both */
|
||||
{NULL}
|
||||
};
|
||||
|
||||
StrategyNumber
|
||||
RelationGetGISTStrategy(Relation r,
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
{
|
||||
return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc));
|
||||
return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc));
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
bool
|
||||
RelationInvokeGISTStrategy(Relation r,
|
||||
AttrNumber attnum,
|
||||
StrategyNumber s,
|
||||
Datum left,
|
||||
Datum right)
|
||||
AttrNumber attnum,
|
||||
StrategyNumber s,
|
||||
Datum left,
|
||||
Datum right)
|
||||
{
|
||||
return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s,
|
||||
left, right));
|
||||
return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s,
|
||||
left, right));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hash.c--
|
||||
* Implementation of Margo Seltzer's Hashing package for postgres.
|
||||
* Implementation of Margo Seltzer's Hashing package for postgres.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.12 1997/01/10 09:46:13 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.13 1997/09/07 04:37:49 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* This file contains only the public interface routines.
|
||||
* This file contains only the public interface routines.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -26,452 +26,483 @@
|
||||
#include <miscadmin.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
bool BuildingHash = false;
|
||||
bool BuildingHash = false;
|
||||
|
||||
/*
|
||||
* hashbuild() -- build a new hash index.
|
||||
* hashbuild() -- build a new hash index.
|
||||
*
|
||||
* We use a global variable to record the fact that we're creating
|
||||
* a new index. This is used to avoid high-concurrency locking,
|
||||
* since the index won't be visible until this transaction commits
|
||||
* and since building is guaranteed to be single-threaded.
|
||||
* We use a global variable to record the fact that we're creating
|
||||
* a new index. This is used to avoid high-concurrency locking,
|
||||
* since the index won't be visible until this transaction commits
|
||||
* and since building is guaranteed to be single-threaded.
|
||||
*/
|
||||
void
|
||||
hashbuild(Relation heap,
|
||||
Relation index,
|
||||
int natts,
|
||||
AttrNumber *attnum,
|
||||
IndexStrategy istrat,
|
||||
uint16 pcount,
|
||||
Datum *params,
|
||||
FuncIndexInfo *finfo,
|
||||
PredInfo *predInfo)
|
||||
Relation index,
|
||||
int natts,
|
||||
AttrNumber * attnum,
|
||||
IndexStrategy istrat,
|
||||
uint16 pcount,
|
||||
Datum * params,
|
||||
FuncIndexInfo * finfo,
|
||||
PredInfo * predInfo)
|
||||
{
|
||||
HeapScanDesc hscan;
|
||||
Buffer buffer;
|
||||
HeapTuple htup;
|
||||
IndexTuple itup;
|
||||
TupleDesc htupdesc, itupdesc;
|
||||
Datum *attdata;
|
||||
bool *nulls;
|
||||
InsertIndexResult res;
|
||||
int nhtups, nitups;
|
||||
int i;
|
||||
HashItem hitem;
|
||||
HeapScanDesc hscan;
|
||||
Buffer buffer;
|
||||
HeapTuple htup;
|
||||
IndexTuple itup;
|
||||
TupleDesc htupdesc,
|
||||
itupdesc;
|
||||
Datum *attdata;
|
||||
bool *nulls;
|
||||
InsertIndexResult res;
|
||||
int nhtups,
|
||||
nitups;
|
||||
int i;
|
||||
HashItem hitem;
|
||||
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
ExprContext *econtext;
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
ExprContext *econtext;
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
|
||||
#endif
|
||||
Oid hrelid, irelid;
|
||||
Node *pred, *oldPred;
|
||||
|
||||
/* note that this is a new btree */
|
||||
BuildingHash = true;
|
||||
|
||||
pred = predInfo->pred;
|
||||
oldPred = predInfo->oldPred;
|
||||
|
||||
/* initialize the hash index metadata page (if this is a new index) */
|
||||
if (oldPred == NULL)
|
||||
_hash_metapinit(index);
|
||||
|
||||
/* get tuple descriptors for heap and index relations */
|
||||
htupdesc = RelationGetTupleDescriptor(heap);
|
||||
itupdesc = RelationGetTupleDescriptor(index);
|
||||
|
||||
/* get space for data items that'll appear in the index tuple */
|
||||
attdata = (Datum *) palloc(natts * sizeof(Datum));
|
||||
nulls = (bool *) palloc(natts * sizeof(bool));
|
||||
|
||||
/*
|
||||
* If this is a predicate (partial) index, we will need to evaluate the
|
||||
* predicate using ExecQual, which requires the current tuple to be in a
|
||||
* slot of a TupleTable. In addition, ExecQual must have an ExprContext
|
||||
* referring to that slot. Here, we initialize dummy TupleTable and
|
||||
* ExprContext objects for this purpose. --Nels, Feb '92
|
||||
*/
|
||||
Oid hrelid,
|
||||
irelid;
|
||||
Node *pred,
|
||||
*oldPred;
|
||||
|
||||
/* note that this is a new btree */
|
||||
BuildingHash = true;
|
||||
|
||||
pred = predInfo->pred;
|
||||
oldPred = predInfo->oldPred;
|
||||
|
||||
/* initialize the hash index metadata page (if this is a new index) */
|
||||
if (oldPred == NULL)
|
||||
_hash_metapinit(index);
|
||||
|
||||
/* get tuple descriptors for heap and index relations */
|
||||
htupdesc = RelationGetTupleDescriptor(heap);
|
||||
itupdesc = RelationGetTupleDescriptor(index);
|
||||
|
||||
/* get space for data items that'll appear in the index tuple */
|
||||
attdata = (Datum *) palloc(natts * sizeof(Datum));
|
||||
nulls = (bool *) palloc(natts * sizeof(bool));
|
||||
|
||||
/*
|
||||
* If this is a predicate (partial) index, we will need to evaluate
|
||||
* the predicate using ExecQual, which requires the current tuple to
|
||||
* be in a slot of a TupleTable. In addition, ExecQual must have an
|
||||
* ExprContext referring to that slot. Here, we initialize dummy
|
||||
* TupleTable and ExprContext objects for this purpose. --Nels, Feb
|
||||
* '92
|
||||
*/
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
if (pred != NULL || oldPred != NULL) {
|
||||
tupleTable = ExecCreateTupleTable(1);
|
||||
slot = ExecAllocTableSlot(tupleTable);
|
||||
econtext = makeNode(ExprContext);
|
||||
FillDummyExprContext(econtext, slot, htupdesc, buffer);
|
||||
}
|
||||
else /* quiet the compiler */
|
||||
if (pred != NULL || oldPred != NULL)
|
||||
{
|
||||
tupleTable = ExecCreateTupleTable(1);
|
||||
slot = ExecAllocTableSlot(tupleTable);
|
||||
econtext = makeNode(ExprContext);
|
||||
FillDummyExprContext(econtext, slot, htupdesc, buffer);
|
||||
}
|
||||
else
|
||||
/* quiet the compiler */
|
||||
{
|
||||
econtext = NULL;
|
||||
tupleTable = 0;
|
||||
slot = 0;
|
||||
}
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
|
||||
/* start a heap scan */
|
||||
hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
|
||||
htup = heap_getnext(hscan, 0, &buffer);
|
||||
|
||||
/* build the index */
|
||||
nhtups = nitups = 0;
|
||||
|
||||
for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) {
|
||||
|
||||
nhtups++;
|
||||
|
||||
/*
|
||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||
* this tuple if it was already in the existing partial index
|
||||
*/
|
||||
if (oldPred != NULL) {
|
||||
/*SetSlotContents(slot, htup); */
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
slot->val = htup;
|
||||
if (ExecQual((List*)oldPred, econtext) == true) {
|
||||
nitups++;
|
||||
continue;
|
||||
}
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
}
|
||||
|
||||
/* Skip this tuple if it doesn't satisfy the partial-index predicate */
|
||||
if (pred != NULL) {
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
/*SetSlotContents(slot, htup); */
|
||||
slot->val = htup;
|
||||
if (ExecQual((List*)pred, econtext) == false)
|
||||
continue;
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
}
|
||||
|
||||
nitups++;
|
||||
|
||||
/*
|
||||
* For the current heap tuple, extract all the attributes
|
||||
* we use in this index, and note which are null.
|
||||
*/
|
||||
for (i = 1; i <= natts; i++) {
|
||||
int attoff;
|
||||
bool attnull;
|
||||
|
||||
/*
|
||||
* Offsets are from the start of the tuple, and are
|
||||
* zero-based; indices are one-based. The next call
|
||||
* returns i - 1. That's data hiding for you.
|
||||
*/
|
||||
|
||||
/* attoff = i - 1 */
|
||||
attoff = AttrNumberGetAttrOffset(i);
|
||||
|
||||
/* below, attdata[attoff] set to equal some datum &
|
||||
* attnull is changed to indicate whether or not the attribute
|
||||
* is null for this tuple
|
||||
*/
|
||||
attdata[attoff] = GetIndexValue(htup,
|
||||
htupdesc,
|
||||
attoff,
|
||||
attnum,
|
||||
finfo,
|
||||
&attnull,
|
||||
buffer);
|
||||
nulls[attoff] = (attnull ? 'n' : ' ');
|
||||
}
|
||||
|
||||
/* form an index tuple and point it at the heap tuple */
|
||||
itup = index_formtuple(itupdesc, attdata, nulls);
|
||||
|
||||
/*
|
||||
* If the single index key is null, we don't insert it into
|
||||
* the index. Hash tables support scans on '='.
|
||||
* Relational algebra says that A = B
|
||||
* returns null if either A or B is null. This
|
||||
* means that no qualification used in an index scan could ever
|
||||
* return true on a null attribute. It also means that indices
|
||||
* can't be used by ISNULL or NOTNULL scans, but that's an
|
||||
* artifact of the strategy map architecture chosen in 1986, not
|
||||
* of the way nulls are handled here.
|
||||
*/
|
||||
|
||||
if (itup->t_info & INDEX_NULL_MASK) {
|
||||
pfree(itup);
|
||||
continue;
|
||||
}
|
||||
|
||||
itup->t_tid = htup->t_ctid;
|
||||
hitem = _hash_formitem(itup);
|
||||
res = _hash_doinsert(index, hitem);
|
||||
pfree(hitem);
|
||||
pfree(itup);
|
||||
pfree(res);
|
||||
}
|
||||
|
||||
/* okay, all heap tuples are indexed */
|
||||
heap_endscan(hscan);
|
||||
|
||||
if (pred != NULL || oldPred != NULL) {
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
ExecDestroyTupleTable(tupleTable, true);
|
||||
pfree(econtext);
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we just counted the tuples in the heap, we update its
|
||||
* stats in pg_class to guarantee that the planner takes advantage
|
||||
* of the index we just created. Finally, only update statistics
|
||||
* during normal index definitions, not for indices on system catalogs
|
||||
* created during bootstrap processing. We must close the relations
|
||||
* before updatings statistics to guarantee that the relcache entries
|
||||
* are flushed when we increment the command counter in UpdateStats().
|
||||
*/
|
||||
if (IsNormalProcessingMode())
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
|
||||
/* start a heap scan */
|
||||
hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
|
||||
htup = heap_getnext(hscan, 0, &buffer);
|
||||
|
||||
/* build the index */
|
||||
nhtups = nitups = 0;
|
||||
|
||||
for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer))
|
||||
{
|
||||
hrelid = heap->rd_id;
|
||||
irelid = index->rd_id;
|
||||
heap_close(heap);
|
||||
index_close(index);
|
||||
UpdateStats(hrelid, nhtups, true);
|
||||
UpdateStats(irelid, nitups, false);
|
||||
if (oldPred != NULL) {
|
||||
if (nitups == nhtups) pred = NULL;
|
||||
UpdateIndexPredicate(irelid, oldPred, pred);
|
||||
}
|
||||
|
||||
nhtups++;
|
||||
|
||||
/*
|
||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||
* this tuple if it was already in the existing partial index
|
||||
*/
|
||||
if (oldPred != NULL)
|
||||
{
|
||||
/* SetSlotContents(slot, htup); */
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
slot->val = htup;
|
||||
if (ExecQual((List *) oldPred, econtext) == true)
|
||||
{
|
||||
nitups++;
|
||||
continue;
|
||||
}
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip this tuple if it doesn't satisfy the partial-index
|
||||
* predicate
|
||||
*/
|
||||
if (pred != NULL)
|
||||
{
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
/* SetSlotContents(slot, htup); */
|
||||
slot->val = htup;
|
||||
if (ExecQual((List *) pred, econtext) == false)
|
||||
continue;
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
}
|
||||
|
||||
nitups++;
|
||||
|
||||
/*
|
||||
* For the current heap tuple, extract all the attributes we use
|
||||
* in this index, and note which are null.
|
||||
*/
|
||||
for (i = 1; i <= natts; i++)
|
||||
{
|
||||
int attoff;
|
||||
bool attnull;
|
||||
|
||||
/*
|
||||
* Offsets are from the start of the tuple, and are
|
||||
* zero-based; indices are one-based. The next call returns i
|
||||
* - 1. That's data hiding for you.
|
||||
*/
|
||||
|
||||
/* attoff = i - 1 */
|
||||
attoff = AttrNumberGetAttrOffset(i);
|
||||
|
||||
/*
|
||||
* below, attdata[attoff] set to equal some datum & attnull is
|
||||
* changed to indicate whether or not the attribute is null
|
||||
* for this tuple
|
||||
*/
|
||||
attdata[attoff] = GetIndexValue(htup,
|
||||
htupdesc,
|
||||
attoff,
|
||||
attnum,
|
||||
finfo,
|
||||
&attnull,
|
||||
buffer);
|
||||
nulls[attoff] = (attnull ? 'n' : ' ');
|
||||
}
|
||||
|
||||
/* form an index tuple and point it at the heap tuple */
|
||||
itup = index_formtuple(itupdesc, attdata, nulls);
|
||||
|
||||
/*
|
||||
* If the single index key is null, we don't insert it into the
|
||||
* index. Hash tables support scans on '='. Relational algebra
|
||||
* says that A = B returns null if either A or B is null. This
|
||||
* means that no qualification used in an index scan could ever
|
||||
* return true on a null attribute. It also means that indices
|
||||
* can't be used by ISNULL or NOTNULL scans, but that's an
|
||||
* artifact of the strategy map architecture chosen in 1986, not
|
||||
* of the way nulls are handled here.
|
||||
*/
|
||||
|
||||
if (itup->t_info & INDEX_NULL_MASK)
|
||||
{
|
||||
pfree(itup);
|
||||
continue;
|
||||
}
|
||||
|
||||
itup->t_tid = htup->t_ctid;
|
||||
hitem = _hash_formitem(itup);
|
||||
res = _hash_doinsert(index, hitem);
|
||||
pfree(hitem);
|
||||
pfree(itup);
|
||||
pfree(res);
|
||||
}
|
||||
|
||||
/* be tidy */
|
||||
pfree(nulls);
|
||||
pfree(attdata);
|
||||
|
||||
/* all done */
|
||||
BuildingHash = false;
|
||||
|
||||
/* okay, all heap tuples are indexed */
|
||||
heap_endscan(hscan);
|
||||
|
||||
if (pred != NULL || oldPred != NULL)
|
||||
{
|
||||
#ifndef OMIT_PARTIAL_INDEX
|
||||
ExecDestroyTupleTable(tupleTable, true);
|
||||
pfree(econtext);
|
||||
#endif /* OMIT_PARTIAL_INDEX */
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we just counted the tuples in the heap, we update its stats
|
||||
* in pg_class to guarantee that the planner takes advantage of the
|
||||
* index we just created. Finally, only update statistics during
|
||||
* normal index definitions, not for indices on system catalogs
|
||||
* created during bootstrap processing. We must close the relations
|
||||
* before updatings statistics to guarantee that the relcache entries
|
||||
* are flushed when we increment the command counter in UpdateStats().
|
||||
*/
|
||||
if (IsNormalProcessingMode())
|
||||
{
|
||||
hrelid = heap->rd_id;
|
||||
irelid = index->rd_id;
|
||||
heap_close(heap);
|
||||
index_close(index);
|
||||
UpdateStats(hrelid, nhtups, true);
|
||||
UpdateStats(irelid, nitups, false);
|
||||
if (oldPred != NULL)
|
||||
{
|
||||
if (nitups == nhtups)
|
||||
pred = NULL;
|
||||
UpdateIndexPredicate(irelid, oldPred, pred);
|
||||
}
|
||||
}
|
||||
|
||||
/* be tidy */
|
||||
pfree(nulls);
|
||||
pfree(attdata);
|
||||
|
||||
/* all done */
|
||||
BuildingHash = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* hashinsert() -- insert an index tuple into a hash table.
|
||||
* hashinsert() -- insert an index tuple into a hash table.
|
||||
*
|
||||
* Hash on the index tuple's key, find the appropriate location
|
||||
* for the new tuple, put it there, and return an InsertIndexResult
|
||||
* to the caller.
|
||||
* Hash on the index tuple's key, find the appropriate location
|
||||
* for the new tuple, put it there, and return an InsertIndexResult
|
||||
* to the caller.
|
||||
*/
|
||||
InsertIndexResult
|
||||
hashinsert(Relation rel, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel)
|
||||
hashinsert(Relation rel, Datum * datum, char *nulls, ItemPointer ht_ctid, Relation heapRel)
|
||||
{
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
InsertIndexResult res;
|
||||
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
InsertIndexResult res;
|
||||
|
||||
/* generate an index tuple */
|
||||
itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls);
|
||||
itup->t_tid = *ht_ctid;
|
||||
|
||||
if (itup->t_info & INDEX_NULL_MASK)
|
||||
return ((InsertIndexResult) NULL);
|
||||
|
||||
hitem = _hash_formitem(itup);
|
||||
|
||||
res = _hash_doinsert(rel, hitem);
|
||||
|
||||
pfree(hitem);
|
||||
pfree(itup);
|
||||
|
||||
return (res);
|
||||
/* generate an index tuple */
|
||||
itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls);
|
||||
itup->t_tid = *ht_ctid;
|
||||
|
||||
if (itup->t_info & INDEX_NULL_MASK)
|
||||
return ((InsertIndexResult) NULL);
|
||||
|
||||
hitem = _hash_formitem(itup);
|
||||
|
||||
res = _hash_doinsert(rel, hitem);
|
||||
|
||||
pfree(hitem);
|
||||
pfree(itup);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* hashgettuple() -- Get the next tuple in the scan.
|
||||
* hashgettuple() -- Get the next tuple in the scan.
|
||||
*/
|
||||
char *
|
||||
char *
|
||||
hashgettuple(IndexScanDesc scan, ScanDirection dir)
|
||||
{
|
||||
RetrieveIndexResult res;
|
||||
|
||||
/*
|
||||
* If we've already initialized this scan, we can just advance it
|
||||
* in the appropriate direction. If we haven't done so yet, we
|
||||
* call a routine to get the first item in the scan.
|
||||
*/
|
||||
|
||||
if (ItemPointerIsValid(&(scan->currentItemData)))
|
||||
res = _hash_next(scan, dir);
|
||||
else
|
||||
res = _hash_first(scan, dir);
|
||||
|
||||
return ((char *) res);
|
||||
RetrieveIndexResult res;
|
||||
|
||||
/*
|
||||
* If we've already initialized this scan, we can just advance it in
|
||||
* the appropriate direction. If we haven't done so yet, we call a
|
||||
* routine to get the first item in the scan.
|
||||
*/
|
||||
|
||||
if (ItemPointerIsValid(&(scan->currentItemData)))
|
||||
res = _hash_next(scan, dir);
|
||||
else
|
||||
res = _hash_first(scan, dir);
|
||||
|
||||
return ((char *) res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* hashbeginscan() -- start a scan on a hash index
|
||||
* hashbeginscan() -- start a scan on a hash index
|
||||
*/
|
||||
char *
|
||||
char *
|
||||
hashbeginscan(Relation rel,
|
||||
bool fromEnd,
|
||||
uint16 keysz,
|
||||
ScanKey scankey)
|
||||
bool fromEnd,
|
||||
uint16 keysz,
|
||||
ScanKey scankey)
|
||||
{
|
||||
IndexScanDesc scan;
|
||||
HashScanOpaque so;
|
||||
|
||||
scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
|
||||
so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
|
||||
so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
|
||||
scan->opaque = so;
|
||||
scan->flags = 0x0;
|
||||
|
||||
/* register scan in case we change pages it's using */
|
||||
_hash_regscan(scan);
|
||||
|
||||
return ((char *) scan);
|
||||
IndexScanDesc scan;
|
||||
HashScanOpaque so;
|
||||
|
||||
scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
|
||||
so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
|
||||
so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
|
||||
scan->opaque = so;
|
||||
scan->flags = 0x0;
|
||||
|
||||
/* register scan in case we change pages it's using */
|
||||
_hash_regscan(scan);
|
||||
|
||||
return ((char *) scan);
|
||||
}
|
||||
|
||||
/*
|
||||
* hashrescan() -- rescan an index relation
|
||||
* hashrescan() -- rescan an index relation
|
||||
*/
|
||||
void
|
||||
hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
|
||||
{
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* we hold a read lock on the current page in the scan */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
|
||||
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
|
||||
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
|
||||
so->hashso_mrkbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* reset the scan key */
|
||||
if (scan->numberOfKeys > 0) {
|
||||
memmove(scan->keyData,
|
||||
scankey,
|
||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* we hold a read lock on the current page in the scan */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
|
||||
{
|
||||
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
|
||||
{
|
||||
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
|
||||
so->hashso_mrkbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* reset the scan key */
|
||||
if (scan->numberOfKeys > 0)
|
||||
{
|
||||
memmove(scan->keyData,
|
||||
scankey,
|
||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* hashendscan() -- close down a scan
|
||||
* hashendscan() -- close down a scan
|
||||
*/
|
||||
void
|
||||
hashendscan(IndexScanDesc scan)
|
||||
{
|
||||
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* release any locks we still hold */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
|
||||
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
|
||||
if (BufferIsValid(so->hashso_mrkbuf))
|
||||
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
|
||||
so->hashso_mrkbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* don't need scan registered anymore */
|
||||
_hash_dropscan(scan);
|
||||
|
||||
/* be tidy */
|
||||
pfree (scan->opaque);
|
||||
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* release any locks we still hold */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
|
||||
{
|
||||
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
|
||||
{
|
||||
if (BufferIsValid(so->hashso_mrkbuf))
|
||||
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
|
||||
so->hashso_mrkbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* don't need scan registered anymore */
|
||||
_hash_dropscan(scan);
|
||||
|
||||
/* be tidy */
|
||||
pfree(scan->opaque);
|
||||
}
|
||||
|
||||
/*
|
||||
* hashmarkpos() -- save current scan position
|
||||
* hashmarkpos() -- save current scan position
|
||||
*
|
||||
*/
|
||||
void
|
||||
hashmarkpos(IndexScanDesc scan)
|
||||
{
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
/* see if we ever call this code. if we do, then so_mrkbuf a
|
||||
* useful element in the scan->opaque structure. if this procedure
|
||||
* is never called, so_mrkbuf should be removed from the scan->opaque
|
||||
* structure.
|
||||
*/
|
||||
elog(NOTICE, "Hashmarkpos() called.");
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* release lock on old marked data, if any */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
|
||||
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
|
||||
so->hashso_mrkbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* bump lock on currentItemData and copy to currentMarkData */
|
||||
if (ItemPointerIsValid(&(scan->currentItemData))) {
|
||||
so->hashso_mrkbuf = _hash_getbuf(scan->relation,
|
||||
BufferGetBlockNumber(so->hashso_curbuf),
|
||||
HASH_READ);
|
||||
scan->currentMarkData = scan->currentItemData;
|
||||
}
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
/*
|
||||
* see if we ever call this code. if we do, then so_mrkbuf a useful
|
||||
* element in the scan->opaque structure. if this procedure is never
|
||||
* called, so_mrkbuf should be removed from the scan->opaque
|
||||
* structure.
|
||||
*/
|
||||
elog(NOTICE, "Hashmarkpos() called.");
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* release lock on old marked data, if any */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
|
||||
{
|
||||
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
|
||||
so->hashso_mrkbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* bump lock on currentItemData and copy to currentMarkData */
|
||||
if (ItemPointerIsValid(&(scan->currentItemData)))
|
||||
{
|
||||
so->hashso_mrkbuf = _hash_getbuf(scan->relation,
|
||||
BufferGetBlockNumber(so->hashso_curbuf),
|
||||
HASH_READ);
|
||||
scan->currentMarkData = scan->currentItemData;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* hashrestrpos() -- restore scan to last saved position
|
||||
* hashrestrpos() -- restore scan to last saved position
|
||||
*/
|
||||
void
|
||||
hashrestrpos(IndexScanDesc scan)
|
||||
{
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
/* see if we ever call this code. if we do, then so_mrkbuf a
|
||||
* useful element in the scan->opaque structure. if this procedure
|
||||
* is never called, so_mrkbuf should be removed from the scan->opaque
|
||||
* structure.
|
||||
*/
|
||||
elog(NOTICE, "Hashrestrpos() called.");
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* release lock on current data, if any */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
|
||||
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* bump lock on currentMarkData and copy to currentItemData */
|
||||
if (ItemPointerIsValid(&(scan->currentMarkData))) {
|
||||
so->hashso_curbuf =
|
||||
_hash_getbuf(scan->relation,
|
||||
BufferGetBlockNumber(so->hashso_mrkbuf),
|
||||
HASH_READ);
|
||||
|
||||
scan->currentItemData = scan->currentMarkData;
|
||||
}
|
||||
ItemPointer iptr;
|
||||
HashScanOpaque so;
|
||||
|
||||
/*
|
||||
* see if we ever call this code. if we do, then so_mrkbuf a useful
|
||||
* element in the scan->opaque structure. if this procedure is never
|
||||
* called, so_mrkbuf should be removed from the scan->opaque
|
||||
* structure.
|
||||
*/
|
||||
elog(NOTICE, "Hashrestrpos() called.");
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
|
||||
/* release lock on current data, if any */
|
||||
if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
|
||||
{
|
||||
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(iptr);
|
||||
}
|
||||
|
||||
/* bump lock on currentMarkData and copy to currentItemData */
|
||||
if (ItemPointerIsValid(&(scan->currentMarkData)))
|
||||
{
|
||||
so->hashso_curbuf =
|
||||
_hash_getbuf(scan->relation,
|
||||
BufferGetBlockNumber(so->hashso_mrkbuf),
|
||||
HASH_READ);
|
||||
|
||||
scan->currentItemData = scan->currentMarkData;
|
||||
}
|
||||
}
|
||||
|
||||
/* stubs */
|
||||
void
|
||||
hashdelete(Relation rel, ItemPointer tid)
|
||||
{
|
||||
/* adjust any active scans that will be affected by this deletion */
|
||||
_hash_adjscans(rel, tid);
|
||||
|
||||
/* delete the data from the page */
|
||||
_hash_pagedel(rel, tid);
|
||||
}
|
||||
/* adjust any active scans that will be affected by this deletion */
|
||||
_hash_adjscans(rel, tid);
|
||||
|
||||
/* delete the data from the page */
|
||||
_hash_pagedel(rel, tid);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashfunc.c--
|
||||
* Comparison functions for hash access method.
|
||||
* Comparison functions for hash access method.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.3 1996/11/10 02:57:40 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.4 1997/09/07 04:37:53 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* These functions are stored in pg_amproc. For each operator class
|
||||
* defined on hash tables, they compute the hash value of the argument.
|
||||
* These functions are stored in pg_amproc. For each operator class
|
||||
* defined on hash tables, they compute the hash value of the argument.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -20,206 +20,223 @@
|
||||
|
||||
#include "access/hash.h"
|
||||
|
||||
uint32 hashint2(int16 key)
|
||||
uint32
|
||||
hashint2(int16 key)
|
||||
{
|
||||
return ((uint32) ~key);
|
||||
return ((uint32) ~ key);
|
||||
}
|
||||
|
||||
uint32 hashint4(uint32 key)
|
||||
uint32
|
||||
hashint4(uint32 key)
|
||||
{
|
||||
return (~key);
|
||||
return (~key);
|
||||
}
|
||||
|
||||
/* Hash function from Chris Torek. */
|
||||
uint32 hashfloat4(float32 keyp)
|
||||
uint32
|
||||
hashfloat4(float32 keyp)
|
||||
{
|
||||
int len;
|
||||
int loop;
|
||||
uint32 h;
|
||||
char *kp = (char *) keyp;
|
||||
int len;
|
||||
int loop;
|
||||
uint32 h;
|
||||
char *kp = (char *) keyp;
|
||||
|
||||
len = sizeof(float32data);
|
||||
len = sizeof(float32data);
|
||||
|
||||
#define HASH4a h = (h << 5) - h + *kp++;
|
||||
#define HASH4b h = (h << 5) + h + *kp++;
|
||||
#define HASH4a h = (h << 5) - h + *kp++;
|
||||
#define HASH4b h = (h << 5) + h + *kp++;
|
||||
#define HASH4 HASH4b
|
||||
|
||||
|
||||
h = 0;
|
||||
if (len > 0) {
|
||||
loop = (len + 8 - 1) >> 3;
|
||||
|
||||
switch (len & (8 - 1)) {
|
||||
case 0:
|
||||
do { /* All fall throughs */
|
||||
HASH4;
|
||||
case 7:
|
||||
HASH4;
|
||||
case 6:
|
||||
HASH4;
|
||||
case 5:
|
||||
HASH4;
|
||||
case 4:
|
||||
HASH4;
|
||||
case 3:
|
||||
HASH4;
|
||||
case 2:
|
||||
HASH4;
|
||||
case 1:
|
||||
HASH4;
|
||||
} while (--loop);
|
||||
h = 0;
|
||||
if (len > 0)
|
||||
{
|
||||
loop = (len + 8 - 1) >> 3;
|
||||
|
||||
switch (len & (8 - 1))
|
||||
{
|
||||
case 0:
|
||||
do
|
||||
{ /* All fall throughs */
|
||||
HASH4;
|
||||
case 7:
|
||||
HASH4;
|
||||
case 6:
|
||||
HASH4;
|
||||
case 5:
|
||||
HASH4;
|
||||
case 4:
|
||||
HASH4;
|
||||
case 3:
|
||||
HASH4;
|
||||
case 2:
|
||||
HASH4;
|
||||
case 1:
|
||||
HASH4;
|
||||
} while (--loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (h);
|
||||
}
|
||||
return (h);
|
||||
}
|
||||
|
||||
|
||||
uint32 hashfloat8(float64 keyp)
|
||||
uint32
|
||||
hashfloat8(float64 keyp)
|
||||
{
|
||||
int len;
|
||||
int loop;
|
||||
uint32 h;
|
||||
char *kp = (char *) keyp;
|
||||
int len;
|
||||
int loop;
|
||||
uint32 h;
|
||||
char *kp = (char *) keyp;
|
||||
|
||||
len = sizeof(float64data);
|
||||
len = sizeof(float64data);
|
||||
|
||||
#define HASH4a h = (h << 5) - h + *kp++;
|
||||
#define HASH4b h = (h << 5) + h + *kp++;
|
||||
#define HASH4a h = (h << 5) - h + *kp++;
|
||||
#define HASH4b h = (h << 5) + h + *kp++;
|
||||
#define HASH4 HASH4b
|
||||
|
||||
|
||||
h = 0;
|
||||
if (len > 0) {
|
||||
loop = (len + 8 - 1) >> 3;
|
||||
|
||||
switch (len & (8 - 1)) {
|
||||
case 0:
|
||||
do { /* All fall throughs */
|
||||
HASH4;
|
||||
case 7:
|
||||
HASH4;
|
||||
case 6:
|
||||
HASH4;
|
||||
case 5:
|
||||
HASH4;
|
||||
case 4:
|
||||
HASH4;
|
||||
case 3:
|
||||
HASH4;
|
||||
case 2:
|
||||
HASH4;
|
||||
case 1:
|
||||
HASH4;
|
||||
} while (--loop);
|
||||
h = 0;
|
||||
if (len > 0)
|
||||
{
|
||||
loop = (len + 8 - 1) >> 3;
|
||||
|
||||
switch (len & (8 - 1))
|
||||
{
|
||||
case 0:
|
||||
do
|
||||
{ /* All fall throughs */
|
||||
HASH4;
|
||||
case 7:
|
||||
HASH4;
|
||||
case 6:
|
||||
HASH4;
|
||||
case 5:
|
||||
HASH4;
|
||||
case 4:
|
||||
HASH4;
|
||||
case 3:
|
||||
HASH4;
|
||||
case 2:
|
||||
HASH4;
|
||||
case 1:
|
||||
HASH4;
|
||||
} while (--loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (h);
|
||||
}
|
||||
|
||||
|
||||
uint32 hashoid(Oid key)
|
||||
{
|
||||
return ((uint32) ~key);
|
||||
return (h);
|
||||
}
|
||||
|
||||
|
||||
uint32 hashchar(char key)
|
||||
uint32
|
||||
hashoid(Oid key)
|
||||
{
|
||||
int len;
|
||||
uint32 h;
|
||||
|
||||
len = sizeof(char);
|
||||
|
||||
#define PRIME1 37
|
||||
#define PRIME2 1048583
|
||||
|
||||
h = 0;
|
||||
/* Convert char to integer */
|
||||
h = h * PRIME1 ^ (key - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32 hashchar2(uint16 intkey)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
char *key = (char *) &intkey;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(uint16);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32 hashchar4(uint32 intkey)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
char *key = (char *) &intkey;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(uint32);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32 hashchar8(char *key)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(char8);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32 hashname(NameData *n)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
char *key;
|
||||
|
||||
key = n->data;
|
||||
|
||||
h = 0;
|
||||
len = NAMEDATALEN;
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
return ((uint32) ~ key);
|
||||
}
|
||||
|
||||
|
||||
uint32 hashchar16(char *key)
|
||||
uint32
|
||||
hashchar(char key)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(char16);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
int len;
|
||||
uint32 h;
|
||||
|
||||
len = sizeof(char);
|
||||
|
||||
#define PRIME1 37
|
||||
#define PRIME2 1048583
|
||||
|
||||
h = 0;
|
||||
/* Convert char to integer */
|
||||
h = h * PRIME1 ^ (key - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32
|
||||
hashchar2(uint16 intkey)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
char *key = (char *) &intkey;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(uint16);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32
|
||||
hashchar4(uint32 intkey)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
char *key = (char *) &intkey;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(uint32);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32
|
||||
hashchar8(char *key)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(char8);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
uint32
|
||||
hashname(NameData * n)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
char *key;
|
||||
|
||||
key = n->data;
|
||||
|
||||
h = 0;
|
||||
len = NAMEDATALEN;
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
hashchar16(char *key)
|
||||
{
|
||||
uint32 h;
|
||||
int len;
|
||||
|
||||
h = 0;
|
||||
len = sizeof(char16);
|
||||
/* Convert string to integer */
|
||||
while (len--)
|
||||
h = h * PRIME1 ^ (*key++ - ' ');
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
|
||||
@@ -234,45 +251,49 @@ uint32 hashchar16(char *key)
|
||||
*
|
||||
* "OZ's original sdbm hash"
|
||||
*/
|
||||
uint32 hashtext(struct varlena *key)
|
||||
uint32
|
||||
hashtext(struct varlena * key)
|
||||
{
|
||||
int keylen;
|
||||
char *keydata;
|
||||
uint32 n;
|
||||
int loop;
|
||||
int keylen;
|
||||
char *keydata;
|
||||
uint32 n;
|
||||
int loop;
|
||||
|
||||
keydata = VARDATA(key);
|
||||
keylen = VARSIZE(key);
|
||||
keydata = VARDATA(key);
|
||||
keylen = VARSIZE(key);
|
||||
|
||||
/* keylen includes the four bytes in which string keylength is stored */
|
||||
keylen -= sizeof(VARSIZE(key));
|
||||
/* keylen includes the four bytes in which string keylength is stored */
|
||||
keylen -= sizeof(VARSIZE(key));
|
||||
|
||||
#define HASHC n = *keydata++ + 65599 * n
|
||||
#define HASHC n = *keydata++ + 65599 * n
|
||||
|
||||
n = 0;
|
||||
if (keylen > 0) {
|
||||
loop = (keylen + 8 - 1) >> 3;
|
||||
|
||||
switch (keylen & (8 - 1)) {
|
||||
case 0:
|
||||
do { /* All fall throughs */
|
||||
HASHC;
|
||||
case 7:
|
||||
HASHC;
|
||||
case 6:
|
||||
HASHC;
|
||||
case 5:
|
||||
HASHC;
|
||||
case 4:
|
||||
HASHC;
|
||||
case 3:
|
||||
HASHC;
|
||||
case 2:
|
||||
HASHC;
|
||||
case 1:
|
||||
HASHC;
|
||||
} while (--loop);
|
||||
n = 0;
|
||||
if (keylen > 0)
|
||||
{
|
||||
loop = (keylen + 8 - 1) >> 3;
|
||||
|
||||
switch (keylen & (8 - 1))
|
||||
{
|
||||
case 0:
|
||||
do
|
||||
{ /* All fall throughs */
|
||||
HASHC;
|
||||
case 7:
|
||||
HASHC;
|
||||
case 6:
|
||||
HASHC;
|
||||
case 5:
|
||||
HASHC;
|
||||
case 4:
|
||||
HASHC;
|
||||
case 3:
|
||||
HASHC;
|
||||
case 2:
|
||||
HASHC;
|
||||
case 1:
|
||||
HASHC;
|
||||
} while (--loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashinsert.c--
|
||||
* Item insertion in hash tables for Postgres.
|
||||
* Item insertion in hash tables for Postgres.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.8 1997/08/12 22:51:30 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.9 1997/09/07 04:37:56 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <access/hash.h>
|
||||
#include <storage/bufmgr.h>
|
||||
#include <utils/memutils.h>
|
||||
@@ -22,211 +22,221 @@ static InsertIndexResult _hash_insertonpg(Relation rel, Buffer buf, int keysz, S
|
||||
static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, HashItem hitem);
|
||||
|
||||
/*
|
||||
* _hash_doinsert() -- Handle insertion of a single HashItem in the table.
|
||||
* _hash_doinsert() -- Handle insertion of a single HashItem in the table.
|
||||
*
|
||||
* This routine is called by the public interface routines, hashbuild
|
||||
* and hashinsert. By here, hashitem is filled in, and has a unique
|
||||
* (xid, seqno) pair. The datum to be used as a "key" is in the
|
||||
* hashitem.
|
||||
* This routine is called by the public interface routines, hashbuild
|
||||
* and hashinsert. By here, hashitem is filled in, and has a unique
|
||||
* (xid, seqno) pair. The datum to be used as a "key" is in the
|
||||
* hashitem.
|
||||
*/
|
||||
InsertIndexResult
|
||||
_hash_doinsert(Relation rel, HashItem hitem)
|
||||
{
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
BlockNumber blkno;
|
||||
HashMetaPage metap;
|
||||
IndexTuple itup;
|
||||
InsertIndexResult res;
|
||||
ScanKey itup_scankey;
|
||||
int natts;
|
||||
Page page;
|
||||
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
|
||||
/* we need a scan key to do our search, so build one */
|
||||
itup = &(hitem->hash_itup);
|
||||
if ((natts = rel->rd_rel->relnatts) != 1)
|
||||
elog(WARN, "Hash indices valid for only one index key.");
|
||||
itup_scankey = _hash_mkscankey(rel, itup, metap);
|
||||
|
||||
/*
|
||||
* find the first page in the bucket chain containing this key and
|
||||
* place it in buf. _hash_search obtains a read lock for us.
|
||||
*/
|
||||
_hash_search(rel, natts, itup_scankey, &buf, metap);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
BlockNumber blkno;
|
||||
HashMetaPage metap;
|
||||
IndexTuple itup;
|
||||
InsertIndexResult res;
|
||||
ScanKey itup_scankey;
|
||||
int natts;
|
||||
Page page;
|
||||
|
||||
/*
|
||||
* trade in our read lock for a write lock so that we can do the
|
||||
* insertion.
|
||||
*/
|
||||
blkno = BufferGetBlockNumber(buf);
|
||||
_hash_relbuf(rel, buf, HASH_READ);
|
||||
buf = _hash_getbuf(rel, blkno, HASH_WRITE);
|
||||
|
||||
|
||||
/*
|
||||
* XXX btree comment (haven't decided what to do in hash): don't
|
||||
* think the bucket can be split while we're reading the metapage.
|
||||
*
|
||||
* If the page was split between the time that we surrendered our
|
||||
* read lock and acquired our write lock, then this page may no
|
||||
* longer be the right place for the key we want to insert.
|
||||
*/
|
||||
|
||||
/* do the insertion */
|
||||
res = _hash_insertonpg(rel, buf, natts, itup_scankey,
|
||||
hitem, metabuf);
|
||||
|
||||
/* be tidy */
|
||||
_hash_freeskey(itup_scankey);
|
||||
|
||||
return (res);
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
|
||||
/* we need a scan key to do our search, so build one */
|
||||
itup = &(hitem->hash_itup);
|
||||
if ((natts = rel->rd_rel->relnatts) != 1)
|
||||
elog(WARN, "Hash indices valid for only one index key.");
|
||||
itup_scankey = _hash_mkscankey(rel, itup, metap);
|
||||
|
||||
/*
|
||||
* find the first page in the bucket chain containing this key and
|
||||
* place it in buf. _hash_search obtains a read lock for us.
|
||||
*/
|
||||
_hash_search(rel, natts, itup_scankey, &buf, metap);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
|
||||
/*
|
||||
* trade in our read lock for a write lock so that we can do the
|
||||
* insertion.
|
||||
*/
|
||||
blkno = BufferGetBlockNumber(buf);
|
||||
_hash_relbuf(rel, buf, HASH_READ);
|
||||
buf = _hash_getbuf(rel, blkno, HASH_WRITE);
|
||||
|
||||
|
||||
/*
|
||||
* XXX btree comment (haven't decided what to do in hash): don't think
|
||||
* the bucket can be split while we're reading the metapage.
|
||||
*
|
||||
* If the page was split between the time that we surrendered our read
|
||||
* lock and acquired our write lock, then this page may no longer be
|
||||
* the right place for the key we want to insert.
|
||||
*/
|
||||
|
||||
/* do the insertion */
|
||||
res = _hash_insertonpg(rel, buf, natts, itup_scankey,
|
||||
hitem, metabuf);
|
||||
|
||||
/* be tidy */
|
||||
_hash_freeskey(itup_scankey);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* _hash_insertonpg() -- Insert a tuple on a particular page in the table.
|
||||
* _hash_insertonpg() -- Insert a tuple on a particular page in the table.
|
||||
*
|
||||
* This recursive procedure does the following things:
|
||||
* This recursive procedure does the following things:
|
||||
*
|
||||
* + if necessary, splits the target page.
|
||||
* + inserts the tuple.
|
||||
* + if necessary, splits the target page.
|
||||
* + inserts the tuple.
|
||||
*
|
||||
* On entry, we must have the right buffer on which to do the
|
||||
* insertion, and the buffer must be pinned and locked. On return,
|
||||
* we will have dropped both the pin and the write lock on the buffer.
|
||||
* On entry, we must have the right buffer on which to do the
|
||||
* insertion, and the buffer must be pinned and locked. On return,
|
||||
* we will have dropped both the pin and the write lock on the buffer.
|
||||
*
|
||||
*/
|
||||
static InsertIndexResult
|
||||
static InsertIndexResult
|
||||
_hash_insertonpg(Relation rel,
|
||||
Buffer buf,
|
||||
int keysz,
|
||||
ScanKey scankey,
|
||||
HashItem hitem,
|
||||
Buffer metabuf)
|
||||
Buffer buf,
|
||||
int keysz,
|
||||
ScanKey scankey,
|
||||
HashItem hitem,
|
||||
Buffer metabuf)
|
||||
{
|
||||
InsertIndexResult res;
|
||||
Page page;
|
||||
BlockNumber itup_blkno;
|
||||
OffsetNumber itup_off;
|
||||
int itemsz;
|
||||
HashPageOpaque pageopaque;
|
||||
bool do_expand = false;
|
||||
Buffer ovflbuf;
|
||||
HashMetaPage metap;
|
||||
Bucket bucket;
|
||||
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
|
||||
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
bucket = pageopaque->hasho_bucket;
|
||||
InsertIndexResult res;
|
||||
Page page;
|
||||
BlockNumber itup_blkno;
|
||||
OffsetNumber itup_off;
|
||||
int itemsz;
|
||||
HashPageOpaque pageopaque;
|
||||
bool do_expand = false;
|
||||
Buffer ovflbuf;
|
||||
HashMetaPage metap;
|
||||
Bucket bucket;
|
||||
|
||||
itemsz = IndexTupleDSize(hitem->hash_itup)
|
||||
+ (sizeof(HashItemData) - sizeof(IndexTupleData));
|
||||
itemsz = DOUBLEALIGN(itemsz);
|
||||
|
||||
while (PageGetFreeSpace(page) < itemsz) {
|
||||
/*
|
||||
* no space on this page; check for an overflow page
|
||||
*/
|
||||
if (BlockNumberIsValid(pageopaque->hasho_nextblkno)) {
|
||||
/*
|
||||
* ovfl page exists; go get it. if it doesn't have room,
|
||||
* we'll find out next pass through the loop test above.
|
||||
*/
|
||||
ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno,
|
||||
HASH_WRITE);
|
||||
_hash_relbuf(rel, buf, HASH_WRITE);
|
||||
buf = ovflbuf;
|
||||
page = BufferGetPage(buf);
|
||||
} else {
|
||||
/*
|
||||
* we're at the end of the bucket chain and we haven't
|
||||
* found a page with enough room. allocate a new overflow
|
||||
* page.
|
||||
*/
|
||||
do_expand = true;
|
||||
ovflbuf = _hash_addovflpage(rel, &metabuf, buf);
|
||||
_hash_relbuf(rel, buf, HASH_WRITE);
|
||||
buf = ovflbuf;
|
||||
page = BufferGetPage(buf);
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
|
||||
if (PageGetFreeSpace(page) < itemsz) {
|
||||
/* it doesn't fit on an empty page -- give up */
|
||||
elog(WARN, "hash item too large");
|
||||
}
|
||||
}
|
||||
_hash_checkpage(page, LH_OVERFLOW_PAGE);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(pageopaque->hasho_bucket == bucket);
|
||||
}
|
||||
bucket = pageopaque->hasho_bucket;
|
||||
|
||||
itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem);
|
||||
itup_blkno = BufferGetBlockNumber(buf);
|
||||
|
||||
/* by here, the new tuple is inserted */
|
||||
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
|
||||
|
||||
ItemPointerSet(&(res->pointerData), itup_blkno, itup_off);
|
||||
|
||||
if (res != NULL) {
|
||||
/*
|
||||
* Increment the number of keys in the table.
|
||||
* We switch lock access type just for a moment
|
||||
* to allow greater accessibility to the metapage.
|
||||
*/
|
||||
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
|
||||
HASH_READ, HASH_WRITE);
|
||||
metap->hashm_nkeys += 1;
|
||||
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
|
||||
HASH_WRITE, HASH_READ);
|
||||
|
||||
}
|
||||
|
||||
_hash_wrtbuf(rel, buf);
|
||||
|
||||
if (do_expand ||
|
||||
(metap->hashm_nkeys / (metap->hashm_maxbucket + 1))
|
||||
> metap->hashm_ffactor) {
|
||||
_hash_expandtable(rel, metabuf);
|
||||
}
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
return (res);
|
||||
}
|
||||
itemsz = IndexTupleDSize(hitem->hash_itup)
|
||||
+ (sizeof(HashItemData) - sizeof(IndexTupleData));
|
||||
itemsz = DOUBLEALIGN(itemsz);
|
||||
|
||||
while (PageGetFreeSpace(page) < itemsz)
|
||||
{
|
||||
|
||||
/*
|
||||
* no space on this page; check for an overflow page
|
||||
*/
|
||||
if (BlockNumberIsValid(pageopaque->hasho_nextblkno))
|
||||
{
|
||||
|
||||
/*
|
||||
* ovfl page exists; go get it. if it doesn't have room,
|
||||
* we'll find out next pass through the loop test above.
|
||||
*/
|
||||
ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno,
|
||||
HASH_WRITE);
|
||||
_hash_relbuf(rel, buf, HASH_WRITE);
|
||||
buf = ovflbuf;
|
||||
page = BufferGetPage(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* we're at the end of the bucket chain and we haven't found a
|
||||
* page with enough room. allocate a new overflow page.
|
||||
*/
|
||||
do_expand = true;
|
||||
ovflbuf = _hash_addovflpage(rel, &metabuf, buf);
|
||||
_hash_relbuf(rel, buf, HASH_WRITE);
|
||||
buf = ovflbuf;
|
||||
page = BufferGetPage(buf);
|
||||
|
||||
if (PageGetFreeSpace(page) < itemsz)
|
||||
{
|
||||
/* it doesn't fit on an empty page -- give up */
|
||||
elog(WARN, "hash item too large");
|
||||
}
|
||||
}
|
||||
_hash_checkpage(page, LH_OVERFLOW_PAGE);
|
||||
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(pageopaque->hasho_bucket == bucket);
|
||||
}
|
||||
|
||||
itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem);
|
||||
itup_blkno = BufferGetBlockNumber(buf);
|
||||
|
||||
/* by here, the new tuple is inserted */
|
||||
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
|
||||
|
||||
ItemPointerSet(&(res->pointerData), itup_blkno, itup_off);
|
||||
|
||||
if (res != NULL)
|
||||
{
|
||||
|
||||
/*
|
||||
* Increment the number of keys in the table. We switch lock
|
||||
* access type just for a moment to allow greater accessibility to
|
||||
* the metapage.
|
||||
*/
|
||||
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
|
||||
HASH_READ, HASH_WRITE);
|
||||
metap->hashm_nkeys += 1;
|
||||
metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
|
||||
HASH_WRITE, HASH_READ);
|
||||
|
||||
}
|
||||
|
||||
_hash_wrtbuf(rel, buf);
|
||||
|
||||
if (do_expand ||
|
||||
(metap->hashm_nkeys / (metap->hashm_maxbucket + 1))
|
||||
> metap->hashm_ffactor)
|
||||
{
|
||||
_hash_expandtable(rel, metabuf);
|
||||
}
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* _hash_pgaddtup() -- add a tuple to a particular page in the index.
|
||||
* _hash_pgaddtup() -- add a tuple to a particular page in the index.
|
||||
*
|
||||
* This routine adds the tuple to the page as requested, and keeps the
|
||||
* write lock and reference associated with the page's buffer. It is
|
||||
* an error to call pgaddtup() without a write lock and reference.
|
||||
* This routine adds the tuple to the page as requested, and keeps the
|
||||
* write lock and reference associated with the page's buffer. It is
|
||||
* an error to call pgaddtup() without a write lock and reference.
|
||||
*/
|
||||
static OffsetNumber
|
||||
static OffsetNumber
|
||||
_hash_pgaddtup(Relation rel,
|
||||
Buffer buf,
|
||||
int keysz,
|
||||
ScanKey itup_scankey,
|
||||
Size itemsize,
|
||||
HashItem hitem)
|
||||
Buffer buf,
|
||||
int keysz,
|
||||
ScanKey itup_scankey,
|
||||
Size itemsize,
|
||||
HashItem hitem)
|
||||
{
|
||||
OffsetNumber itup_off;
|
||||
Page page;
|
||||
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
|
||||
OffsetNumber itup_off;
|
||||
Page page;
|
||||
|
||||
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
|
||||
PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED);
|
||||
|
||||
/* write the buffer, but hold our lock */
|
||||
_hash_wrtnorelbuf(rel, buf);
|
||||
|
||||
return (itup_off);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||
|
||||
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
|
||||
PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED);
|
||||
|
||||
/* write the buffer, but hold our lock */
|
||||
_hash_wrtnorelbuf(rel, buf);
|
||||
|
||||
return (itup_off);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,160 +1,167 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashscan.c--
|
||||
* manage scans on hash tables
|
||||
* manage scans on hash tables
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.8 1996/11/15 18:36:31 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.9 1997/09/07 04:38:01 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Because we can be doing an index scan on a relation while we
|
||||
* update it, we need to avoid missing data that moves around in
|
||||
* the index. The routines and global variables in this file
|
||||
* guarantee that all scans in the local address space stay
|
||||
* correctly positioned. This is all we need to worry about, since
|
||||
* write locking guarantees that no one else will be on the same
|
||||
* page at the same time as we are.
|
||||
* Because we can be doing an index scan on a relation while we
|
||||
* update it, we need to avoid missing data that moves around in
|
||||
* the index. The routines and global variables in this file
|
||||
* guarantee that all scans in the local address space stay
|
||||
* correctly positioned. This is all we need to worry about, since
|
||||
* write locking guarantees that no one else will be on the same
|
||||
* page at the same time as we are.
|
||||
*
|
||||
* The scheme is to manage a list of active scans in the current
|
||||
* backend. Whenever we add or remove records from an index, we
|
||||
* check the list of active scans to see if any has been affected.
|
||||
* A scan is affected only if it is on the same relation, and the
|
||||
* same page, as the update.
|
||||
* The scheme is to manage a list of active scans in the current
|
||||
* backend. Whenever we add or remove records from an index, we
|
||||
* check the list of active scans to see if any has been affected.
|
||||
* A scan is affected only if it is on the same relation, and the
|
||||
* same page, as the update.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <access/hash.h>
|
||||
|
||||
static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
|
||||
typedef struct HashScanListData {
|
||||
IndexScanDesc hashsl_scan;
|
||||
struct HashScanListData *hashsl_next;
|
||||
} HashScanListData;
|
||||
typedef struct HashScanListData
|
||||
{
|
||||
IndexScanDesc hashsl_scan;
|
||||
struct HashScanListData *hashsl_next;
|
||||
} HashScanListData;
|
||||
|
||||
typedef HashScanListData *HashScanList;
|
||||
typedef HashScanListData *HashScanList;
|
||||
|
||||
static HashScanList HashScans = (HashScanList) NULL;
|
||||
static HashScanList HashScans = (HashScanList) NULL;
|
||||
|
||||
/*
|
||||
* _Hash_regscan() -- register a new scan.
|
||||
* _Hash_regscan() -- register a new scan.
|
||||
*/
|
||||
void
|
||||
_hash_regscan(IndexScanDesc scan)
|
||||
{
|
||||
HashScanList new_el;
|
||||
|
||||
new_el = (HashScanList) palloc(sizeof(HashScanListData));
|
||||
new_el->hashsl_scan = scan;
|
||||
new_el->hashsl_next = HashScans;
|
||||
HashScans = new_el;
|
||||
HashScanList new_el;
|
||||
|
||||
new_el = (HashScanList) palloc(sizeof(HashScanListData));
|
||||
new_el->hashsl_scan = scan;
|
||||
new_el->hashsl_next = HashScans;
|
||||
HashScans = new_el;
|
||||
}
|
||||
|
||||
/*
|
||||
* _hash_dropscan() -- drop a scan from the scan list
|
||||
* _hash_dropscan() -- drop a scan from the scan list
|
||||
*/
|
||||
void
|
||||
_hash_dropscan(IndexScanDesc scan)
|
||||
{
|
||||
HashScanList chk, last;
|
||||
|
||||
last = (HashScanList) NULL;
|
||||
for (chk = HashScans;
|
||||
chk != (HashScanList) NULL && chk->hashsl_scan != scan;
|
||||
chk = chk->hashsl_next) {
|
||||
last = chk;
|
||||
}
|
||||
|
||||
if (chk == (HashScanList) NULL)
|
||||
elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
|
||||
|
||||
if (last == (HashScanList) NULL)
|
||||
HashScans = chk->hashsl_next;
|
||||
else
|
||||
last->hashsl_next = chk->hashsl_next;
|
||||
|
||||
pfree (chk);
|
||||
HashScanList chk,
|
||||
last;
|
||||
|
||||
last = (HashScanList) NULL;
|
||||
for (chk = HashScans;
|
||||
chk != (HashScanList) NULL && chk->hashsl_scan != scan;
|
||||
chk = chk->hashsl_next)
|
||||
{
|
||||
last = chk;
|
||||
}
|
||||
|
||||
if (chk == (HashScanList) NULL)
|
||||
elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
|
||||
|
||||
if (last == (HashScanList) NULL)
|
||||
HashScans = chk->hashsl_next;
|
||||
else
|
||||
last->hashsl_next = chk->hashsl_next;
|
||||
|
||||
pfree(chk);
|
||||
}
|
||||
|
||||
void
|
||||
_hash_adjscans(Relation rel, ItemPointer tid)
|
||||
{
|
||||
HashScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = rel->rd_id;
|
||||
for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next) {
|
||||
if (relid == l->hashsl_scan->relation->rd_id)
|
||||
_hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid),
|
||||
ItemPointerGetOffsetNumber(tid));
|
||||
}
|
||||
HashScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = rel->rd_id;
|
||||
for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next)
|
||||
{
|
||||
if (relid == l->hashsl_scan->relation->rd_id)
|
||||
_hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid),
|
||||
ItemPointerGetOffsetNumber(tid));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
|
||||
{
|
||||
ItemPointer current;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
HashScanOpaque so;
|
||||
|
||||
if (!_hash_scantouched(scan, blkno, offno))
|
||||
return;
|
||||
|
||||
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
buf = so->hashso_curbuf;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno) {
|
||||
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
||||
so->hashso_curbuf = buf;
|
||||
}
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno) {
|
||||
ItemPointerData tmp;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
||||
so->hashso_mrkbuf = buf;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
}
|
||||
ItemPointer current;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
HashScanOpaque so;
|
||||
|
||||
if (!_hash_scantouched(scan, blkno, offno))
|
||||
return;
|
||||
|
||||
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
buf = so->hashso_curbuf;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
{
|
||||
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
||||
so->hashso_curbuf = buf;
|
||||
}
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
{
|
||||
ItemPointerData tmp;
|
||||
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
||||
so->hashso_mrkbuf = buf;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
static bool
|
||||
_hash_scantouched(IndexScanDesc scan,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offno)
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offno)
|
||||
{
|
||||
ItemPointer current;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
ItemPointer current;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
@@ -1,423 +1,467 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashsearch.c--
|
||||
* search code for postgres hash tables
|
||||
* search code for postgres hash tables
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.10 1997/06/28 05:45:40 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.11 1997/09/07 04:38:02 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <access/hash.h>
|
||||
#include <storage/bufmgr.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include "regex/utils.h"
|
||||
#include "regex/utils.h"
|
||||
#else
|
||||
# include <string.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* _hash_search() -- Finds the page/bucket that the contains the
|
||||
* scankey and loads it into *bufP. the buffer has a read lock.
|
||||
* _hash_search() -- Finds the page/bucket that the contains the
|
||||
* scankey and loads it into *bufP. the buffer has a read lock.
|
||||
*/
|
||||
void
|
||||
_hash_search(Relation rel,
|
||||
int keysz,
|
||||
ScanKey scankey,
|
||||
Buffer *bufP,
|
||||
HashMetaPage metap)
|
||||
int keysz,
|
||||
ScanKey scankey,
|
||||
Buffer * bufP,
|
||||
HashMetaPage metap)
|
||||
{
|
||||
BlockNumber blkno;
|
||||
Datum keyDatum;
|
||||
Bucket bucket;
|
||||
BlockNumber blkno;
|
||||
Datum keyDatum;
|
||||
Bucket bucket;
|
||||
|
||||
if (scankey == (ScanKey) NULL ||
|
||||
(keyDatum = scankey[0].sk_argument) == (Datum) NULL) {
|
||||
/*
|
||||
* If the scankey argument is NULL, all tuples will satisfy
|
||||
* the scan so we start the scan at the first bucket (bucket
|
||||
* 0).
|
||||
*/
|
||||
bucket = 0;
|
||||
} else {
|
||||
bucket = _hash_call(rel, metap, keyDatum);
|
||||
}
|
||||
if (scankey == (ScanKey) NULL ||
|
||||
(keyDatum = scankey[0].sk_argument) == (Datum) NULL)
|
||||
{
|
||||
|
||||
blkno = BUCKET_TO_BLKNO(bucket);
|
||||
|
||||
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
/*
|
||||
* If the scankey argument is NULL, all tuples will satisfy the
|
||||
* scan so we start the scan at the first bucket (bucket 0).
|
||||
*/
|
||||
bucket = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bucket = _hash_call(rel, metap, keyDatum);
|
||||
}
|
||||
|
||||
blkno = BUCKET_TO_BLKNO(bucket);
|
||||
|
||||
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
}
|
||||
|
||||
/*
|
||||
* _hash_next() -- Get the next item in a scan.
|
||||
* _hash_next() -- Get the next item in a scan.
|
||||
*
|
||||
* On entry, we have a valid currentItemData in the scan, and a
|
||||
* read lock on the page that contains that item. We do not have
|
||||
* the page pinned. We return the next item in the scan. On
|
||||
* exit, we have the page containing the next item locked but not
|
||||
* pinned.
|
||||
* On entry, we have a valid currentItemData in the scan, and a
|
||||
* read lock on the page that contains that item. We do not have
|
||||
* the page pinned. We return the next item in the scan. On
|
||||
* exit, we have the page containing the next item locked but not
|
||||
* pinned.
|
||||
*/
|
||||
RetrieveIndexResult
|
||||
_hash_next(IndexScanDesc scan, ScanDirection dir)
|
||||
{
|
||||
Relation rel;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
Page page;
|
||||
OffsetNumber offnum;
|
||||
RetrieveIndexResult res;
|
||||
ItemPointer current;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
HashScanOpaque so;
|
||||
Relation rel;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
Page page;
|
||||
OffsetNumber offnum;
|
||||
RetrieveIndexResult res;
|
||||
ItemPointer current;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
HashScanOpaque so;
|
||||
|
||||
rel = scan->relation;
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
current = &(scan->currentItemData);
|
||||
rel = scan->relation;
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
current = &(scan->currentItemData);
|
||||
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
|
||||
/*
|
||||
* XXX 10 may 91: somewhere there's a bug in our management of the
|
||||
* cached buffer for this scan. wei discovered it. the following
|
||||
* is a workaround so he can work until i figure out what's going on.
|
||||
*/
|
||||
/*
|
||||
* XXX 10 may 91: somewhere there's a bug in our management of the
|
||||
* cached buffer for this scan. wei discovered it. the following is
|
||||
* a workaround so he can work until i figure out what's going on.
|
||||
*/
|
||||
|
||||
if (!BufferIsValid(so->hashso_curbuf)) {
|
||||
so->hashso_curbuf = _hash_getbuf(rel,
|
||||
ItemPointerGetBlockNumber(current),
|
||||
HASH_READ);
|
||||
}
|
||||
if (!BufferIsValid(so->hashso_curbuf))
|
||||
{
|
||||
so->hashso_curbuf = _hash_getbuf(rel,
|
||||
ItemPointerGetBlockNumber(current),
|
||||
HASH_READ);
|
||||
}
|
||||
|
||||
/* we still have the buffer pinned and locked */
|
||||
buf = so->hashso_curbuf;
|
||||
/* we still have the buffer pinned and locked */
|
||||
buf = so->hashso_curbuf;
|
||||
|
||||
/*
|
||||
* step to next valid tuple. note that _hash_step releases our
|
||||
* lock on 'metabuf'; if we switch to a new 'buf' while looking
|
||||
* for the next tuple, we come back with a lock on that buffer.
|
||||
*/
|
||||
if (!_hash_step(scan, &buf, dir, metabuf)) {
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
/*
|
||||
* step to next valid tuple. note that _hash_step releases our lock
|
||||
* on 'metabuf'; if we switch to a new 'buf' while looking for the
|
||||
* next tuple, we come back with a lock on that buffer.
|
||||
*/
|
||||
if (!_hash_step(scan, &buf, dir, metabuf))
|
||||
{
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
|
||||
/* if we're here, _hash_step found a valid tuple */
|
||||
current = &(scan->currentItemData);
|
||||
offnum = ItemPointerGetOffsetNumber(current);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
|
||||
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
itup = &hitem->hash_itup;
|
||||
res = FormRetrieveIndexResult(current, &(itup->t_tid));
|
||||
/* if we're here, _hash_step found a valid tuple */
|
||||
current = &(scan->currentItemData);
|
||||
offnum = ItemPointerGetOffsetNumber(current);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
itup = &hitem->hash_itup;
|
||||
res = FormRetrieveIndexResult(current, &(itup->t_tid));
|
||||
|
||||
return (res);
|
||||
return (res);
|
||||
}
|
||||
|
||||
static void
|
||||
_hash_readnext(Relation rel,
|
||||
Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
|
||||
Buffer * bufp, Page * pagep, HashPageOpaque * opaquep)
|
||||
{
|
||||
BlockNumber blkno;
|
||||
BlockNumber blkno;
|
||||
|
||||
blkno = (*opaquep)->hasho_nextblkno;
|
||||
_hash_relbuf(rel, *bufp, HASH_READ);
|
||||
*bufp = InvalidBuffer;
|
||||
if (BlockNumberIsValid(blkno)) {
|
||||
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
*pagep = BufferGetPage(*bufp);
|
||||
_hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
|
||||
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
|
||||
Assert(!PageIsEmpty(*pagep));
|
||||
}
|
||||
blkno = (*opaquep)->hasho_nextblkno;
|
||||
_hash_relbuf(rel, *bufp, HASH_READ);
|
||||
*bufp = InvalidBuffer;
|
||||
if (BlockNumberIsValid(blkno))
|
||||
{
|
||||
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
*pagep = BufferGetPage(*bufp);
|
||||
_hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
|
||||
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
|
||||
Assert(!PageIsEmpty(*pagep));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_hash_readprev(Relation rel,
|
||||
Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
|
||||
Buffer * bufp, Page * pagep, HashPageOpaque * opaquep)
|
||||
{
|
||||
BlockNumber blkno;
|
||||
BlockNumber blkno;
|
||||
|
||||
blkno = (*opaquep)->hasho_prevblkno;
|
||||
_hash_relbuf(rel, *bufp, HASH_READ);
|
||||
*bufp = InvalidBuffer;
|
||||
if (BlockNumberIsValid(blkno)) {
|
||||
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
*pagep = BufferGetPage(*bufp);
|
||||
_hash_checkpage(*pagep, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
|
||||
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
|
||||
if (PageIsEmpty(*pagep)) {
|
||||
Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE);
|
||||
_hash_relbuf(rel, *bufp, HASH_READ);
|
||||
*bufp = InvalidBuffer;
|
||||
blkno = (*opaquep)->hasho_prevblkno;
|
||||
_hash_relbuf(rel, *bufp, HASH_READ);
|
||||
*bufp = InvalidBuffer;
|
||||
if (BlockNumberIsValid(blkno))
|
||||
{
|
||||
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
*pagep = BufferGetPage(*bufp);
|
||||
_hash_checkpage(*pagep, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
|
||||
if (PageIsEmpty(*pagep))
|
||||
{
|
||||
Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE);
|
||||
_hash_relbuf(rel, *bufp, HASH_READ);
|
||||
*bufp = InvalidBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _hash_first() -- Find the first item in a scan.
|
||||
* _hash_first() -- Find the first item in a scan.
|
||||
*
|
||||
* Return the RetrieveIndexResult of the first item in the tree that
|
||||
* satisfies the qualificatin associated with the scan descriptor. On
|
||||
* exit, the page containing the current index tuple is read locked
|
||||
* and pinned, and the scan's opaque data entry is updated to
|
||||
* include the buffer.
|
||||
* Return the RetrieveIndexResult of the first item in the tree that
|
||||
* satisfies the qualificatin associated with the scan descriptor. On
|
||||
* exit, the page containing the current index tuple is read locked
|
||||
* and pinned, and the scan's opaque data entry is updated to
|
||||
* include the buffer.
|
||||
*/
|
||||
RetrieveIndexResult
|
||||
_hash_first(IndexScanDesc scan, ScanDirection dir)
|
||||
{
|
||||
Relation rel;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
Page page;
|
||||
HashPageOpaque opaque;
|
||||
HashMetaPage metap;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
ItemPointer current;
|
||||
OffsetNumber offnum;
|
||||
RetrieveIndexResult res;
|
||||
HashScanOpaque so;
|
||||
Relation rel;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
Page page;
|
||||
HashPageOpaque opaque;
|
||||
HashMetaPage metap;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
ItemPointer current;
|
||||
OffsetNumber offnum;
|
||||
RetrieveIndexResult res;
|
||||
HashScanOpaque so;
|
||||
|
||||
rel = scan->relation;
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
current = &(scan->currentItemData);
|
||||
rel = scan->relation;
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
current = &(scan->currentItemData);
|
||||
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
|
||||
/*
|
||||
* XXX -- The attribute number stored in the scan key is the attno
|
||||
* in the heap relation. We need to transmogrify this into
|
||||
* the index relation attno here. For the moment, we have
|
||||
* hardwired attno == 1.
|
||||
*/
|
||||
/*
|
||||
* XXX -- The attribute number stored in the scan key is the attno in
|
||||
* the heap relation. We need to transmogrify this into the index
|
||||
* relation attno here. For the moment, we have hardwired attno == 1.
|
||||
*/
|
||||
|
||||
/* find the correct bucket page and load it into buf */
|
||||
_hash_search(rel, 1, scan->keyData, &buf, metap);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
/* find the correct bucket page and load it into buf */
|
||||
_hash_search(rel, 1, scan->keyData, &buf, metap);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
|
||||
/*
|
||||
* if we are scanning forward, we need to find the first non-empty
|
||||
* page (if any) in the bucket chain. since overflow pages are
|
||||
* never empty, this had better be either the bucket page or the
|
||||
* first overflow page.
|
||||
*
|
||||
* if we are scanning backward, we always go all the way to the
|
||||
* end of the bucket chain.
|
||||
*/
|
||||
if (PageIsEmpty(page)) {
|
||||
if (BlockNumberIsValid(opaque->hasho_nextblkno)) {
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
} else {
|
||||
ItemPointerSetInvalid(current);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
/*
|
||||
* If there is no scankeys, all tuples will satisfy
|
||||
* the scan - so we continue in _hash_step to get
|
||||
* tuples from all buckets. - vadim 04/29/97
|
||||
*/
|
||||
if ( scan->numberOfKeys >= 1 )
|
||||
{
|
||||
_hash_relbuf(rel, buf, HASH_READ);
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
/*
|
||||
* if we are scanning forward, we need to find the first non-empty
|
||||
* page (if any) in the bucket chain. since overflow pages are never
|
||||
* empty, this had better be either the bucket page or the first
|
||||
* overflow page.
|
||||
*
|
||||
* if we are scanning backward, we always go all the way to the end of
|
||||
* the bucket chain.
|
||||
*/
|
||||
if (PageIsEmpty(page))
|
||||
{
|
||||
if (BlockNumberIsValid(opaque->hasho_nextblkno))
|
||||
{
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPointerSetInvalid(current);
|
||||
so->hashso_curbuf = InvalidBuffer;
|
||||
|
||||
/*
|
||||
* If there is no scankeys, all tuples will satisfy the scan -
|
||||
* so we continue in _hash_step to get tuples from all
|
||||
* buckets. - vadim 04/29/97
|
||||
*/
|
||||
if (scan->numberOfKeys >= 1)
|
||||
{
|
||||
_hash_relbuf(rel, buf, HASH_READ);
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
while (BlockNumberIsValid(opaque->hasho_nextblkno)) {
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
while (BlockNumberIsValid(opaque->hasho_nextblkno))
|
||||
{
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_hash_step(scan, &buf, dir, metabuf)) {
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
if (!_hash_step(scan, &buf, dir, metabuf))
|
||||
{
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
|
||||
/* if we're here, _hash_step found a valid tuple */
|
||||
current = &(scan->currentItemData);
|
||||
offnum = ItemPointerGetOffsetNumber(current);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
|
||||
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
itup = &hitem->hash_itup;
|
||||
res = FormRetrieveIndexResult(current, &(itup->t_tid));
|
||||
/* if we're here, _hash_step found a valid tuple */
|
||||
current = &(scan->currentItemData);
|
||||
offnum = ItemPointerGetOffsetNumber(current);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
itup = &hitem->hash_itup;
|
||||
res = FormRetrieveIndexResult(current, &(itup->t_tid));
|
||||
|
||||
return (res);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* _hash_step() -- step to the next valid item in a scan in the bucket.
|
||||
* _hash_step() -- step to the next valid item in a scan in the bucket.
|
||||
*
|
||||
* If no valid record exists in the requested direction, return
|
||||
* false. Else, return true and set the CurrentItemData for the
|
||||
* scan to the right thing.
|
||||
*
|
||||
* 'bufP' points to the buffer which contains the current page
|
||||
* that we'll step through.
|
||||
* If no valid record exists in the requested direction, return
|
||||
* false. Else, return true and set the CurrentItemData for the
|
||||
* scan to the right thing.
|
||||
*
|
||||
* 'metabuf' is released when this returns.
|
||||
* 'bufP' points to the buffer which contains the current page
|
||||
* that we'll step through.
|
||||
*
|
||||
* 'metabuf' is released when this returns.
|
||||
*/
|
||||
bool
|
||||
_hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf)
|
||||
_hash_step(IndexScanDesc scan, Buffer * bufP, ScanDirection dir, Buffer metabuf)
|
||||
{
|
||||
Relation rel;
|
||||
ItemPointer current;
|
||||
HashScanOpaque so;
|
||||
int allbuckets;
|
||||
HashMetaPage metap;
|
||||
Buffer buf;
|
||||
Page page;
|
||||
HashPageOpaque opaque;
|
||||
OffsetNumber maxoff;
|
||||
OffsetNumber offnum;
|
||||
Bucket bucket;
|
||||
BlockNumber blkno;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
Relation rel;
|
||||
ItemPointer current;
|
||||
HashScanOpaque so;
|
||||
int allbuckets;
|
||||
HashMetaPage metap;
|
||||
Buffer buf;
|
||||
Page page;
|
||||
HashPageOpaque opaque;
|
||||
OffsetNumber maxoff;
|
||||
OffsetNumber offnum;
|
||||
Bucket bucket;
|
||||
BlockNumber blkno;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
|
||||
rel = scan->relation;
|
||||
current = &(scan->currentItemData);
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
allbuckets = (scan->numberOfKeys < 1);
|
||||
rel = scan->relation;
|
||||
current = &(scan->currentItemData);
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
allbuckets = (scan->numberOfKeys < 1);
|
||||
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||
|
||||
buf = *bufP;
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
buf = *bufP;
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
|
||||
/*
|
||||
* If _hash_step is called from _hash_first, current will not be
|
||||
* valid, so we can't dereference it. However, in that case, we
|
||||
* presumably want to start at the beginning/end of the page...
|
||||
*/
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
if (ItemPointerIsValid(current)) {
|
||||
offnum = ItemPointerGetOffsetNumber(current);
|
||||
} else {
|
||||
offnum = InvalidOffsetNumber;
|
||||
}
|
||||
|
||||
/*
|
||||
* 'offnum' now points to the last tuple we have seen (if any).
|
||||
*
|
||||
* continue to step through tuples until:
|
||||
* 1) we get to the end of the bucket chain or
|
||||
* 2) we find a valid tuple.
|
||||
*/
|
||||
do {
|
||||
bucket = opaque->hasho_bucket;
|
||||
|
||||
switch (dir) {
|
||||
case ForwardScanDirection:
|
||||
if (offnum != InvalidOffsetNumber) {
|
||||
offnum = OffsetNumberNext(offnum); /* move forward */
|
||||
} else {
|
||||
offnum = FirstOffsetNumber; /* new page */
|
||||
}
|
||||
while (offnum > maxoff) {
|
||||
/*
|
||||
* either this page is empty (maxoff ==
|
||||
* InvalidOffsetNumber) or we ran off the end.
|
||||
*/
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
if (BufferIsInvalid(buf)) { /* end of chain */
|
||||
if (allbuckets && bucket < metap->hashm_maxbucket) {
|
||||
++bucket;
|
||||
blkno = BUCKET_TO_BLKNO(bucket);
|
||||
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(opaque->hasho_bucket == bucket);
|
||||
while (PageIsEmpty(page) &&
|
||||
BlockNumberIsValid(opaque->hasho_nextblkno)) {
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
}
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
offnum = FirstOffsetNumber;
|
||||
} else {
|
||||
maxoff = offnum = InvalidOffsetNumber;
|
||||
break; /* while */
|
||||
}
|
||||
} else {
|
||||
/* _hash_readnext never returns an empty page */
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
offnum = FirstOffsetNumber;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BackwardScanDirection:
|
||||
if (offnum != InvalidOffsetNumber) {
|
||||
offnum = OffsetNumberPrev(offnum); /* move back */
|
||||
} else {
|
||||
offnum = maxoff; /* new page */
|
||||
}
|
||||
while (offnum < FirstOffsetNumber) {
|
||||
/*
|
||||
* either this page is empty (offnum ==
|
||||
* InvalidOffsetNumber) or we ran off the end.
|
||||
*/
|
||||
_hash_readprev(rel, &buf, &page, &opaque);
|
||||
if (BufferIsInvalid(buf)) { /* end of chain */
|
||||
if (allbuckets && bucket > 0) {
|
||||
--bucket;
|
||||
blkno = BUCKET_TO_BLKNO(bucket);
|
||||
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(opaque->hasho_bucket == bucket);
|
||||
while (BlockNumberIsValid(opaque->hasho_nextblkno)) {
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
}
|
||||
maxoff = offnum = PageGetMaxOffsetNumber(page);
|
||||
} else {
|
||||
maxoff = offnum = InvalidOffsetNumber;
|
||||
break; /* while */
|
||||
}
|
||||
} else {
|
||||
/* _hash_readprev never returns an empty page */
|
||||
maxoff = offnum = PageGetMaxOffsetNumber(page);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* NoMovementScanDirection */
|
||||
/* this should not be reached */
|
||||
break;
|
||||
/*
|
||||
* If _hash_step is called from _hash_first, current will not be
|
||||
* valid, so we can't dereference it. However, in that case, we
|
||||
* presumably want to start at the beginning/end of the page...
|
||||
*/
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
if (ItemPointerIsValid(current))
|
||||
{
|
||||
offnum = ItemPointerGetOffsetNumber(current);
|
||||
}
|
||||
else
|
||||
{
|
||||
offnum = InvalidOffsetNumber;
|
||||
}
|
||||
|
||||
/* we ran off the end of the world without finding a match */
|
||||
if (offnum == InvalidOffsetNumber) {
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
*bufP = so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(current);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/* get ready to check this tuple */
|
||||
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
itup = &hitem->hash_itup;
|
||||
} while (!_hash_checkqual(scan, itup));
|
||||
|
||||
/* if we made it to here, we've found a valid tuple */
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
blkno = BufferGetBlockNumber(buf);
|
||||
*bufP = so->hashso_curbuf = buf;
|
||||
ItemPointerSet(current, blkno, offnum);
|
||||
return(true);
|
||||
/*
|
||||
* 'offnum' now points to the last tuple we have seen (if any).
|
||||
*
|
||||
* continue to step through tuples until: 1) we get to the end of the
|
||||
* bucket chain or 2) we find a valid tuple.
|
||||
*/
|
||||
do
|
||||
{
|
||||
bucket = opaque->hasho_bucket;
|
||||
|
||||
switch (dir)
|
||||
{
|
||||
case ForwardScanDirection:
|
||||
if (offnum != InvalidOffsetNumber)
|
||||
{
|
||||
offnum = OffsetNumberNext(offnum); /* move forward */
|
||||
}
|
||||
else
|
||||
{
|
||||
offnum = FirstOffsetNumber; /* new page */
|
||||
}
|
||||
while (offnum > maxoff)
|
||||
{
|
||||
|
||||
/*
|
||||
* either this page is empty (maxoff ==
|
||||
* InvalidOffsetNumber) or we ran off the end.
|
||||
*/
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
if (BufferIsInvalid(buf))
|
||||
{ /* end of chain */
|
||||
if (allbuckets && bucket < metap->hashm_maxbucket)
|
||||
{
|
||||
++bucket;
|
||||
blkno = BUCKET_TO_BLKNO(bucket);
|
||||
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(opaque->hasho_bucket == bucket);
|
||||
while (PageIsEmpty(page) &&
|
||||
BlockNumberIsValid(opaque->hasho_nextblkno))
|
||||
{
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
}
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
offnum = FirstOffsetNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxoff = offnum = InvalidOffsetNumber;
|
||||
break; /* while */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* _hash_readnext never returns an empty page */
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
offnum = FirstOffsetNumber;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BackwardScanDirection:
|
||||
if (offnum != InvalidOffsetNumber)
|
||||
{
|
||||
offnum = OffsetNumberPrev(offnum); /* move back */
|
||||
}
|
||||
else
|
||||
{
|
||||
offnum = maxoff;/* new page */
|
||||
}
|
||||
while (offnum < FirstOffsetNumber)
|
||||
{
|
||||
|
||||
/*
|
||||
* either this page is empty (offnum ==
|
||||
* InvalidOffsetNumber) or we ran off the end.
|
||||
*/
|
||||
_hash_readprev(rel, &buf, &page, &opaque);
|
||||
if (BufferIsInvalid(buf))
|
||||
{ /* end of chain */
|
||||
if (allbuckets && bucket > 0)
|
||||
{
|
||||
--bucket;
|
||||
blkno = BUCKET_TO_BLKNO(bucket);
|
||||
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
||||
page = BufferGetPage(buf);
|
||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(opaque->hasho_bucket == bucket);
|
||||
while (BlockNumberIsValid(opaque->hasho_nextblkno))
|
||||
{
|
||||
_hash_readnext(rel, &buf, &page, &opaque);
|
||||
}
|
||||
maxoff = offnum = PageGetMaxOffsetNumber(page);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxoff = offnum = InvalidOffsetNumber;
|
||||
break; /* while */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* _hash_readprev never returns an empty page */
|
||||
maxoff = offnum = PageGetMaxOffsetNumber(page);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* NoMovementScanDirection */
|
||||
/* this should not be reached */
|
||||
break;
|
||||
}
|
||||
|
||||
/* we ran off the end of the world without finding a match */
|
||||
if (offnum == InvalidOffsetNumber)
|
||||
{
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
*bufP = so->hashso_curbuf = InvalidBuffer;
|
||||
ItemPointerSetInvalid(current);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* get ready to check this tuple */
|
||||
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
itup = &hitem->hash_itup;
|
||||
} while (!_hash_checkqual(scan, itup));
|
||||
|
||||
/* if we made it to here, we've found a valid tuple */
|
||||
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||
blkno = BufferGetBlockNumber(buf);
|
||||
*bufP = so->hashso_curbuf = buf;
|
||||
ItemPointerSet(current, blkno, offnum);
|
||||
return (true);
|
||||
}
|
||||
|
||||
@@ -1,80 +1,83 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* btstrat.c--
|
||||
* Srategy map entries for the btree indexed access method
|
||||
* Srategy map entries for the btree indexed access method
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.9 1997/08/20 02:01:42 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.10 1997/09/07 04:38:03 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <access/hash.h>
|
||||
#include <access/istrat.h>
|
||||
|
||||
/*
|
||||
* only one valid strategy for hash tables: equality.
|
||||
/*
|
||||
* only one valid strategy for hash tables: equality.
|
||||
*/
|
||||
|
||||
#ifdef NOT_USED
|
||||
static StrategyNumber HTNegate[1] = {
|
||||
InvalidStrategy
|
||||
static StrategyNumber HTNegate[1] = {
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
static StrategyNumber HTCommute[1] = {
|
||||
HTEqualStrategyNumber
|
||||
static StrategyNumber HTCommute[1] = {
|
||||
HTEqualStrategyNumber
|
||||
};
|
||||
|
||||
static StrategyNumber HTNegateCommute[1] = {
|
||||
InvalidStrategy
|
||||
static StrategyNumber HTNegateCommute[1] = {
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
static StrategyEvaluationData HTEvaluationData = {
|
||||
/* XXX static for simplicity */
|
||||
static StrategyEvaluationData HTEvaluationData = {
|
||||
/* XXX static for simplicity */
|
||||
|
||||
HTMaxStrategyNumber,
|
||||
(StrategyTransformMap)HTNegate,
|
||||
(StrategyTransformMap)HTCommute,
|
||||
(StrategyTransformMap)HTNegateCommute,
|
||||
{NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}
|
||||
HTMaxStrategyNumber,
|
||||
(StrategyTransformMap) HTNegate,
|
||||
(StrategyTransformMap) HTCommute,
|
||||
(StrategyTransformMap) HTNegateCommute,
|
||||
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* RelationGetHashStrategy
|
||||
* RelationGetHashStrategy
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef NOT_USED
|
||||
static StrategyNumber
|
||||
static StrategyNumber
|
||||
_hash_getstrat(Relation rel,
|
||||
AttrNumber attno,
|
||||
RegProcedure proc)
|
||||
AttrNumber attno,
|
||||
RegProcedure proc)
|
||||
{
|
||||
StrategyNumber strat;
|
||||
StrategyNumber strat;
|
||||
|
||||
strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc);
|
||||
strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc);
|
||||
|
||||
Assert(StrategyNumberIsValid(strat));
|
||||
Assert(StrategyNumberIsValid(strat));
|
||||
|
||||
return (strat);
|
||||
return (strat);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
static bool
|
||||
static bool
|
||||
_hash_invokestrat(Relation rel,
|
||||
AttrNumber attno,
|
||||
StrategyNumber strat,
|
||||
Datum left,
|
||||
Datum right)
|
||||
AttrNumber attno,
|
||||
StrategyNumber strat,
|
||||
Datum left,
|
||||
Datum right)
|
||||
{
|
||||
return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat,
|
||||
left, right));
|
||||
return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat,
|
||||
left, right));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,109 +1,110 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* btutils.c--
|
||||
* Utility code for Postgres btree implementation.
|
||||
* Utility code for Postgres btree implementation.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.9 1997/08/14 05:01:32 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.10 1997/09/07 04:38:04 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <access/hash.h>
|
||||
#include <fmgr.h>
|
||||
#include <utils/memutils.h>
|
||||
#include <access/iqual.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
ScanKey
|
||||
_hash_mkscankey(Relation rel, IndexTuple itup, HashMetaPage metap)
|
||||
{
|
||||
ScanKey skey;
|
||||
TupleDesc itupdesc;
|
||||
int natts;
|
||||
AttrNumber i;
|
||||
Datum arg;
|
||||
RegProcedure proc;
|
||||
bool null;
|
||||
|
||||
natts = rel->rd_rel->relnatts;
|
||||
itupdesc = RelationGetTupleDescriptor(rel);
|
||||
|
||||
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
|
||||
|
||||
for (i = 0; i < natts; i++) {
|
||||
arg = index_getattr(itup, i + 1, itupdesc, &null);
|
||||
proc = metap->hashm_procid;
|
||||
ScanKeyEntryInitialize(&skey[i],
|
||||
0x0, (AttrNumber) (i + 1), proc, arg);
|
||||
}
|
||||
|
||||
return (skey);
|
||||
}
|
||||
ScanKey skey;
|
||||
TupleDesc itupdesc;
|
||||
int natts;
|
||||
AttrNumber i;
|
||||
Datum arg;
|
||||
RegProcedure proc;
|
||||
bool null;
|
||||
|
||||
natts = rel->rd_rel->relnatts;
|
||||
itupdesc = RelationGetTupleDescriptor(rel);
|
||||
|
||||
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
|
||||
|
||||
for (i = 0; i < natts; i++)
|
||||
{
|
||||
arg = index_getattr(itup, i + 1, itupdesc, &null);
|
||||
proc = metap->hashm_procid;
|
||||
ScanKeyEntryInitialize(&skey[i],
|
||||
0x0, (AttrNumber) (i + 1), proc, arg);
|
||||
}
|
||||
|
||||
return (skey);
|
||||
}
|
||||
|
||||
void
|
||||
_hash_freeskey(ScanKey skey)
|
||||
{
|
||||
pfree(skey);
|
||||
pfree(skey);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
_hash_checkqual(IndexScanDesc scan, IndexTuple itup)
|
||||
{
|
||||
if (scan->numberOfKeys > 0)
|
||||
return (index_keytest(itup,
|
||||
RelationGetTupleDescriptor(scan->relation),
|
||||
scan->numberOfKeys, scan->keyData));
|
||||
else
|
||||
return (true);
|
||||
if (scan->numberOfKeys > 0)
|
||||
return (index_keytest(itup,
|
||||
RelationGetTupleDescriptor(scan->relation),
|
||||
scan->numberOfKeys, scan->keyData));
|
||||
else
|
||||
return (true);
|
||||
}
|
||||
|
||||
HashItem
|
||||
_hash_formitem(IndexTuple itup)
|
||||
{
|
||||
int nbytes_hitem;
|
||||
HashItem hitem;
|
||||
Size tuplen;
|
||||
|
||||
/* disallow nulls in hash keys */
|
||||
if (itup->t_info & INDEX_NULL_MASK)
|
||||
elog(WARN, "hash indices cannot include null keys");
|
||||
|
||||
/* make a copy of the index tuple with room for the sequence number */
|
||||
tuplen = IndexTupleSize(itup);
|
||||
nbytes_hitem = tuplen +
|
||||
(sizeof(HashItemData) - sizeof(IndexTupleData));
|
||||
|
||||
hitem = (HashItem) palloc(nbytes_hitem);
|
||||
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
|
||||
|
||||
return (hitem);
|
||||
int nbytes_hitem;
|
||||
HashItem hitem;
|
||||
Size tuplen;
|
||||
|
||||
/* disallow nulls in hash keys */
|
||||
if (itup->t_info & INDEX_NULL_MASK)
|
||||
elog(WARN, "hash indices cannot include null keys");
|
||||
|
||||
/* make a copy of the index tuple with room for the sequence number */
|
||||
tuplen = IndexTupleSize(itup);
|
||||
nbytes_hitem = tuplen +
|
||||
(sizeof(HashItemData) - sizeof(IndexTupleData));
|
||||
|
||||
hitem = (HashItem) palloc(nbytes_hitem);
|
||||
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
|
||||
|
||||
return (hitem);
|
||||
}
|
||||
|
||||
Bucket
|
||||
_hash_call(Relation rel, HashMetaPage metap, Datum key)
|
||||
{
|
||||
uint32 n;
|
||||
Bucket bucket;
|
||||
RegProcedure proc;
|
||||
|
||||
proc = metap->hashm_procid;
|
||||
n = (uint32) fmgr(proc, key);
|
||||
bucket = n & metap->hashm_highmask;
|
||||
if (bucket > metap->hashm_maxbucket)
|
||||
bucket = bucket & metap->hashm_lowmask;
|
||||
return (bucket);
|
||||
uint32 n;
|
||||
Bucket bucket;
|
||||
RegProcedure proc;
|
||||
|
||||
proc = metap->hashm_procid;
|
||||
n = (uint32) fmgr(proc, key);
|
||||
bucket = n & metap->hashm_highmask;
|
||||
if (bucket > metap->hashm_maxbucket)
|
||||
bucket = bucket & metap->hashm_lowmask;
|
||||
return (bucket);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,12 +113,13 @@ _hash_call(Relation rel, HashMetaPage metap, Datum key)
|
||||
uint32
|
||||
_hash_log2(uint32 num)
|
||||
{
|
||||
uint32 i, limit;
|
||||
|
||||
limit = 1;
|
||||
for (i = 0; limit < num; limit = limit << 1, i++)
|
||||
;
|
||||
return (i);
|
||||
uint32 i,
|
||||
limit;
|
||||
|
||||
limit = 1;
|
||||
for (i = 0; limit < num; limit = limit << 1, i++)
|
||||
;
|
||||
return (i);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -126,19 +128,20 @@ _hash_log2(uint32 num)
|
||||
void
|
||||
_hash_checkpage(Page page, int flags)
|
||||
{
|
||||
HashPageOpaque opaque;
|
||||
HashPageOpaque opaque;
|
||||
|
||||
Assert(page);
|
||||
Assert(((PageHeader)(page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData)));
|
||||
Assert(page);
|
||||
Assert(((PageHeader) (page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData)));
|
||||
#if 1
|
||||
Assert(((PageHeader)(page))->pd_upper <=
|
||||
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
|
||||
Assert(((PageHeader)(page))->pd_special ==
|
||||
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
|
||||
Assert(((PageHeader)(page))->pd_opaque.od_pagesize == BLCKSZ);
|
||||
Assert(((PageHeader) (page))->pd_upper <=
|
||||
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
|
||||
Assert(((PageHeader) (page))->pd_special ==
|
||||
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
|
||||
Assert(((PageHeader) (page))->pd_opaque.od_pagesize == BLCKSZ);
|
||||
#endif
|
||||
if (flags) {
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(opaque->hasho_flag & flags);
|
||||
}
|
||||
if (flags)
|
||||
{
|
||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||
Assert(opaque->hasho_flag & flags);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hio.c--
|
||||
* POSTGRES heap access method input/output code.
|
||||
* POSTGRES heap access method input/output code.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Id: hio.c,v 1.9 1996/11/05 09:53:02 scrappy Exp $
|
||||
* $Id: hio.c,v 1.10 1997/09/07 04:38:11 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -21,64 +21,65 @@
|
||||
|
||||
/*
|
||||
* amputunique - place tuple at tid
|
||||
* Currently on errors, calls elog. Perhaps should return -1?
|
||||
* Possible errors include the addition of a tuple to the page
|
||||
* between the time the linep is chosen and the page is L_UP'd.
|
||||
* Currently on errors, calls elog. Perhaps should return -1?
|
||||
* Possible errors include the addition of a tuple to the page
|
||||
* between the time the linep is chosen and the page is L_UP'd.
|
||||
*
|
||||
* This should be coordinated with the B-tree code.
|
||||
* Probably needs to have an amdelunique to allow for
|
||||
* internal index records to be deleted and reordered as needed.
|
||||
* For the heap AM, this should never be needed.
|
||||
* This should be coordinated with the B-tree code.
|
||||
* Probably needs to have an amdelunique to allow for
|
||||
* internal index records to be deleted and reordered as needed.
|
||||
* For the heap AM, this should never be needed.
|
||||
*/
|
||||
void
|
||||
RelationPutHeapTuple(Relation relation,
|
||||
BlockNumber blockIndex,
|
||||
HeapTuple tuple)
|
||||
BlockNumber blockIndex,
|
||||
HeapTuple tuple)
|
||||
{
|
||||
Buffer buffer;
|
||||
Page pageHeader;
|
||||
BlockNumber numberOfBlocks;
|
||||
OffsetNumber offnum;
|
||||
unsigned int len;
|
||||
ItemId itemId;
|
||||
Item item;
|
||||
|
||||
/* ----------------
|
||||
* increment access statistics
|
||||
* ----------------
|
||||
*/
|
||||
IncrHeapAccessStat(local_RelationPutHeapTuple);
|
||||
IncrHeapAccessStat(global_RelationPutHeapTuple);
|
||||
|
||||
Assert(RelationIsValid(relation));
|
||||
Assert(HeapTupleIsValid(tuple));
|
||||
|
||||
numberOfBlocks = RelationGetNumberOfBlocks(relation);
|
||||
Assert(blockIndex < numberOfBlocks);
|
||||
|
||||
buffer = ReadBuffer(relation, blockIndex);
|
||||
Buffer buffer;
|
||||
Page pageHeader;
|
||||
BlockNumber numberOfBlocks;
|
||||
OffsetNumber offnum;
|
||||
unsigned int len;
|
||||
ItemId itemId;
|
||||
Item item;
|
||||
|
||||
/* ----------------
|
||||
* increment access statistics
|
||||
* ----------------
|
||||
*/
|
||||
IncrHeapAccessStat(local_RelationPutHeapTuple);
|
||||
IncrHeapAccessStat(global_RelationPutHeapTuple);
|
||||
|
||||
Assert(RelationIsValid(relation));
|
||||
Assert(HeapTupleIsValid(tuple));
|
||||
|
||||
numberOfBlocks = RelationGetNumberOfBlocks(relation);
|
||||
Assert(blockIndex < numberOfBlocks);
|
||||
|
||||
buffer = ReadBuffer(relation, blockIndex);
|
||||
#ifndef NO_BUFFERISVALID
|
||||
if (!BufferIsValid(buffer)) {
|
||||
elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
|
||||
blockIndex, &relation->rd_rel->relname);
|
||||
}
|
||||
if (!BufferIsValid(buffer))
|
||||
{
|
||||
elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
|
||||
blockIndex, &relation->rd_rel->relname);
|
||||
}
|
||||
#endif
|
||||
|
||||
pageHeader = (Page)BufferGetPage(buffer);
|
||||
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
|
||||
Assert((int)len <= PageGetFreeSpace(pageHeader));
|
||||
|
||||
offnum = PageAddItem((Page)pageHeader, (Item)tuple,
|
||||
tuple->t_len, InvalidOffsetNumber, LP_USED);
|
||||
|
||||
itemId = PageGetItemId((Page)pageHeader, offnum);
|
||||
item = PageGetItem((Page)pageHeader, itemId);
|
||||
|
||||
ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum);
|
||||
|
||||
WriteBuffer(buffer);
|
||||
/* return an accurate tuple */
|
||||
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
|
||||
|
||||
pageHeader = (Page) BufferGetPage(buffer);
|
||||
len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
|
||||
Assert((int) len <= PageGetFreeSpace(pageHeader));
|
||||
|
||||
offnum = PageAddItem((Page) pageHeader, (Item) tuple,
|
||||
tuple->t_len, InvalidOffsetNumber, LP_USED);
|
||||
|
||||
itemId = PageGetItemId((Page) pageHeader, offnum);
|
||||
item = PageGetItem((Page) pageHeader, itemId);
|
||||
|
||||
ItemPointerSet(&((HeapTuple) item)->t_ctid, blockIndex, offnum);
|
||||
|
||||
WriteBuffer(buffer);
|
||||
/* return an accurate tuple */
|
||||
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -91,7 +92,7 @@ RelationPutHeapTuple(Relation relation,
|
||||
* Eventually, we should cache the number of blocks in a relation somewhere.
|
||||
* Until that time, this code will have to do an lseek to determine the number
|
||||
* of blocks in a relation.
|
||||
*
|
||||
*
|
||||
* This code should ideally do at most 4 semops, 1 lseek, and possibly 1 write
|
||||
* to do an append; it's possible to eliminate 2 of the semops if we do direct
|
||||
* buffer stuff (!); the lseek and the write can go if we get
|
||||
@@ -107,70 +108,70 @@ RelationPutHeapTuple(Relation relation,
|
||||
void
|
||||
RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple)
|
||||
{
|
||||
Buffer buffer;
|
||||
Page pageHeader;
|
||||
BlockNumber lastblock;
|
||||
OffsetNumber offnum;
|
||||
unsigned int len;
|
||||
ItemId itemId;
|
||||
Item item;
|
||||
|
||||
Assert(RelationIsValid(relation));
|
||||
Assert(HeapTupleIsValid(tuple));
|
||||
|
||||
/*
|
||||
* XXX This does an lseek - VERY expensive - but at the moment it
|
||||
* is the only way to accurately determine how many blocks are in
|
||||
* a relation. A good optimization would be to get this to actually
|
||||
* work properly.
|
||||
*/
|
||||
|
||||
lastblock = RelationGetNumberOfBlocks(relation);
|
||||
|
||||
if (lastblock == 0)
|
||||
Buffer buffer;
|
||||
Page pageHeader;
|
||||
BlockNumber lastblock;
|
||||
OffsetNumber offnum;
|
||||
unsigned int len;
|
||||
ItemId itemId;
|
||||
Item item;
|
||||
|
||||
Assert(RelationIsValid(relation));
|
||||
Assert(HeapTupleIsValid(tuple));
|
||||
|
||||
/*
|
||||
* XXX This does an lseek - VERY expensive - but at the moment it is
|
||||
* the only way to accurately determine how many blocks are in a
|
||||
* relation. A good optimization would be to get this to actually
|
||||
* work properly.
|
||||
*/
|
||||
|
||||
lastblock = RelationGetNumberOfBlocks(relation);
|
||||
|
||||
if (lastblock == 0)
|
||||
{
|
||||
buffer = ReadBuffer(relation, lastblock);
|
||||
pageHeader = (Page)BufferGetPage(buffer);
|
||||
if (PageIsNew((PageHeader) pageHeader))
|
||||
buffer = ReadBuffer(relation, lastblock);
|
||||
pageHeader = (Page) BufferGetPage(buffer);
|
||||
if (PageIsNew((PageHeader) pageHeader))
|
||||
{
|
||||
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
|
||||
pageHeader = (Page)BufferGetPage(buffer);
|
||||
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
|
||||
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
|
||||
pageHeader = (Page) BufferGetPage(buffer);
|
||||
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
buffer = ReadBuffer(relation, lastblock - 1);
|
||||
|
||||
pageHeader = (Page)BufferGetPage(buffer);
|
||||
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
|
||||
|
||||
/*
|
||||
* Note that this is true if the above returned a bogus page, which
|
||||
* it will do for a completely empty relation.
|
||||
*/
|
||||
|
||||
if (len > PageGetFreeSpace(pageHeader))
|
||||
else
|
||||
buffer = ReadBuffer(relation, lastblock - 1);
|
||||
|
||||
pageHeader = (Page) BufferGetPage(buffer);
|
||||
len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
|
||||
|
||||
/*
|
||||
* Note that this is true if the above returned a bogus page, which it
|
||||
* will do for a completely empty relation.
|
||||
*/
|
||||
|
||||
if (len > PageGetFreeSpace(pageHeader))
|
||||
{
|
||||
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
|
||||
pageHeader = (Page)BufferGetPage(buffer);
|
||||
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
|
||||
|
||||
if (len > PageGetFreeSpace(pageHeader))
|
||||
elog(WARN, "Tuple is too big: size %d", len);
|
||||
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
|
||||
pageHeader = (Page) BufferGetPage(buffer);
|
||||
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
|
||||
|
||||
if (len > PageGetFreeSpace(pageHeader))
|
||||
elog(WARN, "Tuple is too big: size %d", len);
|
||||
}
|
||||
|
||||
offnum = PageAddItem((Page)pageHeader, (Item)tuple,
|
||||
tuple->t_len, InvalidOffsetNumber, LP_USED);
|
||||
|
||||
itemId = PageGetItemId((Page)pageHeader, offnum);
|
||||
item = PageGetItem((Page)pageHeader, itemId);
|
||||
|
||||
lastblock = BufferGetBlockNumber(buffer);
|
||||
|
||||
ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum);
|
||||
|
||||
/* return an accurate tuple */
|
||||
ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
|
||||
|
||||
WriteBuffer(buffer);
|
||||
|
||||
offnum = PageAddItem((Page) pageHeader, (Item) tuple,
|
||||
tuple->t_len, InvalidOffsetNumber, LP_USED);
|
||||
|
||||
itemId = PageGetItemId((Page) pageHeader, offnum);
|
||||
item = PageGetItem((Page) pageHeader, itemId);
|
||||
|
||||
lastblock = BufferGetBlockNumber(buffer);
|
||||
|
||||
ItemPointerSet(&((HeapTuple) item)->t_ctid, lastblock, offnum);
|
||||
|
||||
/* return an accurate tuple */
|
||||
ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
|
||||
|
||||
WriteBuffer(buffer);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* stats.c--
|
||||
* heap access method debugging statistic collection routines
|
||||
* heap access method debugging statistic collection routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.11 1997/08/19 21:29:21 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.12 1997/09/07 04:38:13 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* initam should be moved someplace else.
|
||||
* initam should be moved someplace else.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -23,322 +23,327 @@
|
||||
#include <utils/mcxt.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
static void InitHeapAccessStatistics(void);
|
||||
static void InitHeapAccessStatistics(void);
|
||||
|
||||
/* ----------------
|
||||
* InitHeapAccessStatistics
|
||||
* InitHeapAccessStatistics
|
||||
* ----------------
|
||||
*/
|
||||
HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL;
|
||||
|
||||
|
||||
static void
|
||||
InitHeapAccessStatistics()
|
||||
InitHeapAccessStatistics()
|
||||
{
|
||||
MemoryContext oldContext;
|
||||
HeapAccessStatistics stats;
|
||||
|
||||
/* ----------------
|
||||
* make sure we don't initialize things twice
|
||||
* ----------------
|
||||
*/
|
||||
if (heap_access_stats != NULL)
|
||||
return;
|
||||
|
||||
/* ----------------
|
||||
* allocate statistics structure from the top memory context
|
||||
* ----------------
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(TopMemoryContext);
|
||||
|
||||
stats = (HeapAccessStatistics)
|
||||
palloc(sizeof(HeapAccessStatisticsData));
|
||||
|
||||
/* ----------------
|
||||
* initialize fields to default values
|
||||
* ----------------
|
||||
*/
|
||||
stats->global_open = 0;
|
||||
stats->global_openr = 0;
|
||||
stats->global_close = 0;
|
||||
stats->global_beginscan = 0;
|
||||
stats->global_rescan = 0;
|
||||
stats->global_endscan = 0;
|
||||
stats->global_getnext = 0;
|
||||
stats->global_fetch = 0;
|
||||
stats->global_insert = 0;
|
||||
stats->global_delete = 0;
|
||||
stats->global_replace = 0;
|
||||
stats->global_markpos = 0;
|
||||
stats->global_restrpos = 0;
|
||||
stats->global_BufferGetRelation = 0;
|
||||
stats->global_RelationIdGetRelation = 0;
|
||||
stats->global_RelationIdGetRelation_Buf = 0;
|
||||
stats->global_getreldesc = 0;
|
||||
stats->global_heapgettup = 0;
|
||||
stats->global_RelationPutHeapTuple = 0;
|
||||
stats->global_RelationPutLongHeapTuple = 0;
|
||||
|
||||
stats->local_open = 0;
|
||||
stats->local_openr = 0;
|
||||
stats->local_close = 0;
|
||||
stats->local_beginscan = 0;
|
||||
stats->local_rescan = 0;
|
||||
stats->local_endscan = 0;
|
||||
stats->local_getnext = 0;
|
||||
stats->local_fetch = 0;
|
||||
stats->local_insert = 0;
|
||||
stats->local_delete = 0;
|
||||
stats->local_replace = 0;
|
||||
stats->local_markpos = 0;
|
||||
stats->local_restrpos = 0;
|
||||
stats->local_BufferGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation_Buf = 0;
|
||||
stats->local_getreldesc = 0;
|
||||
stats->local_heapgettup = 0;
|
||||
stats->local_RelationPutHeapTuple = 0;
|
||||
stats->local_RelationPutLongHeapTuple = 0;
|
||||
stats->local_RelationNameGetRelation = 0;
|
||||
stats->global_RelationNameGetRelation = 0;
|
||||
|
||||
/* ----------------
|
||||
* record init times
|
||||
* ----------------
|
||||
*/
|
||||
time(&stats->init_global_timestamp);
|
||||
time(&stats->local_reset_timestamp);
|
||||
time(&stats->last_request_timestamp);
|
||||
|
||||
/* ----------------
|
||||
* return to old memory context
|
||||
* ----------------
|
||||
*/
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
heap_access_stats = stats;
|
||||
MemoryContext oldContext;
|
||||
HeapAccessStatistics stats;
|
||||
|
||||
/* ----------------
|
||||
* make sure we don't initialize things twice
|
||||
* ----------------
|
||||
*/
|
||||
if (heap_access_stats != NULL)
|
||||
return;
|
||||
|
||||
/* ----------------
|
||||
* allocate statistics structure from the top memory context
|
||||
* ----------------
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(TopMemoryContext);
|
||||
|
||||
stats = (HeapAccessStatistics)
|
||||
palloc(sizeof(HeapAccessStatisticsData));
|
||||
|
||||
/* ----------------
|
||||
* initialize fields to default values
|
||||
* ----------------
|
||||
*/
|
||||
stats->global_open = 0;
|
||||
stats->global_openr = 0;
|
||||
stats->global_close = 0;
|
||||
stats->global_beginscan = 0;
|
||||
stats->global_rescan = 0;
|
||||
stats->global_endscan = 0;
|
||||
stats->global_getnext = 0;
|
||||
stats->global_fetch = 0;
|
||||
stats->global_insert = 0;
|
||||
stats->global_delete = 0;
|
||||
stats->global_replace = 0;
|
||||
stats->global_markpos = 0;
|
||||
stats->global_restrpos = 0;
|
||||
stats->global_BufferGetRelation = 0;
|
||||
stats->global_RelationIdGetRelation = 0;
|
||||
stats->global_RelationIdGetRelation_Buf = 0;
|
||||
stats->global_getreldesc = 0;
|
||||
stats->global_heapgettup = 0;
|
||||
stats->global_RelationPutHeapTuple = 0;
|
||||
stats->global_RelationPutLongHeapTuple = 0;
|
||||
|
||||
stats->local_open = 0;
|
||||
stats->local_openr = 0;
|
||||
stats->local_close = 0;
|
||||
stats->local_beginscan = 0;
|
||||
stats->local_rescan = 0;
|
||||
stats->local_endscan = 0;
|
||||
stats->local_getnext = 0;
|
||||
stats->local_fetch = 0;
|
||||
stats->local_insert = 0;
|
||||
stats->local_delete = 0;
|
||||
stats->local_replace = 0;
|
||||
stats->local_markpos = 0;
|
||||
stats->local_restrpos = 0;
|
||||
stats->local_BufferGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation_Buf = 0;
|
||||
stats->local_getreldesc = 0;
|
||||
stats->local_heapgettup = 0;
|
||||
stats->local_RelationPutHeapTuple = 0;
|
||||
stats->local_RelationPutLongHeapTuple = 0;
|
||||
stats->local_RelationNameGetRelation = 0;
|
||||
stats->global_RelationNameGetRelation = 0;
|
||||
|
||||
/* ----------------
|
||||
* record init times
|
||||
* ----------------
|
||||
*/
|
||||
time(&stats->init_global_timestamp);
|
||||
time(&stats->local_reset_timestamp);
|
||||
time(&stats->last_request_timestamp);
|
||||
|
||||
/* ----------------
|
||||
* return to old memory context
|
||||
* ----------------
|
||||
*/
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
heap_access_stats = stats;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* ResetHeapAccessStatistics
|
||||
* ResetHeapAccessStatistics
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
ResetHeapAccessStatistics()
|
||||
ResetHeapAccessStatistics()
|
||||
{
|
||||
HeapAccessStatistics stats;
|
||||
|
||||
/* ----------------
|
||||
* do nothing if stats aren't initialized
|
||||
* ----------------
|
||||
*/
|
||||
if (heap_access_stats == NULL)
|
||||
return;
|
||||
|
||||
stats = heap_access_stats;
|
||||
|
||||
/* ----------------
|
||||
* reset local counts
|
||||
* ----------------
|
||||
*/
|
||||
stats->local_open = 0;
|
||||
stats->local_openr = 0;
|
||||
stats->local_close = 0;
|
||||
stats->local_beginscan = 0;
|
||||
stats->local_rescan = 0;
|
||||
stats->local_endscan = 0;
|
||||
stats->local_getnext = 0;
|
||||
stats->local_fetch = 0;
|
||||
stats->local_insert = 0;
|
||||
stats->local_delete = 0;
|
||||
stats->local_replace = 0;
|
||||
stats->local_markpos = 0;
|
||||
stats->local_restrpos = 0;
|
||||
stats->local_BufferGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation_Buf = 0;
|
||||
stats->local_getreldesc = 0;
|
||||
stats->local_heapgettup = 0;
|
||||
stats->local_RelationPutHeapTuple = 0;
|
||||
stats->local_RelationPutLongHeapTuple = 0;
|
||||
|
||||
/* ----------------
|
||||
* reset local timestamps
|
||||
* ----------------
|
||||
*/
|
||||
time(&stats->local_reset_timestamp);
|
||||
time(&stats->last_request_timestamp);
|
||||
HeapAccessStatistics stats;
|
||||
|
||||
/* ----------------
|
||||
* do nothing if stats aren't initialized
|
||||
* ----------------
|
||||
*/
|
||||
if (heap_access_stats == NULL)
|
||||
return;
|
||||
|
||||
stats = heap_access_stats;
|
||||
|
||||
/* ----------------
|
||||
* reset local counts
|
||||
* ----------------
|
||||
*/
|
||||
stats->local_open = 0;
|
||||
stats->local_openr = 0;
|
||||
stats->local_close = 0;
|
||||
stats->local_beginscan = 0;
|
||||
stats->local_rescan = 0;
|
||||
stats->local_endscan = 0;
|
||||
stats->local_getnext = 0;
|
||||
stats->local_fetch = 0;
|
||||
stats->local_insert = 0;
|
||||
stats->local_delete = 0;
|
||||
stats->local_replace = 0;
|
||||
stats->local_markpos = 0;
|
||||
stats->local_restrpos = 0;
|
||||
stats->local_BufferGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation = 0;
|
||||
stats->local_RelationIdGetRelation_Buf = 0;
|
||||
stats->local_getreldesc = 0;
|
||||
stats->local_heapgettup = 0;
|
||||
stats->local_RelationPutHeapTuple = 0;
|
||||
stats->local_RelationPutLongHeapTuple = 0;
|
||||
|
||||
/* ----------------
|
||||
* reset local timestamps
|
||||
* ----------------
|
||||
*/
|
||||
time(&stats->local_reset_timestamp);
|
||||
time(&stats->last_request_timestamp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* GetHeapAccessStatistics
|
||||
* GetHeapAccessStatistics
|
||||
* ----------------
|
||||
*/
|
||||
HeapAccessStatistics GetHeapAccessStatistics()
|
||||
HeapAccessStatistics
|
||||
GetHeapAccessStatistics()
|
||||
{
|
||||
HeapAccessStatistics stats;
|
||||
|
||||
/* ----------------
|
||||
* return nothing if stats aren't initialized
|
||||
* ----------------
|
||||
*/
|
||||
if (heap_access_stats == NULL)
|
||||
return NULL;
|
||||
|
||||
/* ----------------
|
||||
* record the current request time
|
||||
* ----------------
|
||||
*/
|
||||
time(&heap_access_stats->last_request_timestamp);
|
||||
|
||||
/* ----------------
|
||||
* allocate a copy of the stats and return it to the caller.
|
||||
* ----------------
|
||||
*/
|
||||
stats = (HeapAccessStatistics)
|
||||
palloc(sizeof(HeapAccessStatisticsData));
|
||||
|
||||
memmove(stats,
|
||||
heap_access_stats,
|
||||
sizeof(HeapAccessStatisticsData));
|
||||
|
||||
return stats;
|
||||
HeapAccessStatistics stats;
|
||||
|
||||
/* ----------------
|
||||
* return nothing if stats aren't initialized
|
||||
* ----------------
|
||||
*/
|
||||
if (heap_access_stats == NULL)
|
||||
return NULL;
|
||||
|
||||
/* ----------------
|
||||
* record the current request time
|
||||
* ----------------
|
||||
*/
|
||||
time(&heap_access_stats->last_request_timestamp);
|
||||
|
||||
/* ----------------
|
||||
* allocate a copy of the stats and return it to the caller.
|
||||
* ----------------
|
||||
*/
|
||||
stats = (HeapAccessStatistics)
|
||||
palloc(sizeof(HeapAccessStatisticsData));
|
||||
|
||||
memmove(stats,
|
||||
heap_access_stats,
|
||||
sizeof(HeapAccessStatisticsData));
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* PrintHeapAccessStatistics
|
||||
* PrintHeapAccessStatistics
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
PrintHeapAccessStatistics(HeapAccessStatistics stats)
|
||||
{
|
||||
/* ----------------
|
||||
* return nothing if stats aren't valid
|
||||
* ----------------
|
||||
*/
|
||||
if (stats == NULL)
|
||||
return;
|
||||
|
||||
printf("======== heap am statistics ========\n");
|
||||
printf("init_global_timestamp: %s",
|
||||
ctime(&(stats->init_global_timestamp)));
|
||||
|
||||
printf("local_reset_timestamp: %s",
|
||||
ctime(&(stats->local_reset_timestamp)));
|
||||
|
||||
printf("last_request_timestamp: %s",
|
||||
ctime(&(stats->last_request_timestamp)));
|
||||
|
||||
printf("local/global_open: %6d/%6d\n",
|
||||
stats->local_open, stats->global_open);
|
||||
|
||||
printf("local/global_openr: %6d/%6d\n",
|
||||
stats->local_openr, stats->global_openr);
|
||||
|
||||
printf("local/global_close: %6d/%6d\n",
|
||||
stats->local_close, stats->global_close);
|
||||
|
||||
printf("local/global_beginscan: %6d/%6d\n",
|
||||
stats->local_beginscan, stats->global_beginscan);
|
||||
|
||||
printf("local/global_rescan: %6d/%6d\n",
|
||||
stats->local_rescan, stats->global_rescan);
|
||||
|
||||
printf("local/global_endscan: %6d/%6d\n",
|
||||
stats->local_endscan, stats->global_endscan);
|
||||
|
||||
printf("local/global_getnext: %6d/%6d\n",
|
||||
stats->local_getnext, stats->global_getnext);
|
||||
|
||||
printf("local/global_fetch: %6d/%6d\n",
|
||||
stats->local_fetch, stats->global_fetch);
|
||||
|
||||
printf("local/global_insert: %6d/%6d\n",
|
||||
stats->local_insert, stats->global_insert);
|
||||
|
||||
printf("local/global_delete: %6d/%6d\n",
|
||||
stats->local_delete, stats->global_delete);
|
||||
|
||||
printf("local/global_replace: %6d/%6d\n",
|
||||
stats->local_replace, stats->global_replace);
|
||||
|
||||
printf("local/global_markpos: %6d/%6d\n",
|
||||
stats->local_markpos, stats->global_markpos);
|
||||
|
||||
printf("local/global_restrpos: %6d/%6d\n",
|
||||
stats->local_restrpos, stats->global_restrpos);
|
||||
|
||||
printf("================\n");
|
||||
|
||||
printf("local/global_BufferGetRelation: %6d/%6d\n",
|
||||
stats->local_BufferGetRelation,
|
||||
stats->global_BufferGetRelation);
|
||||
|
||||
printf("local/global_RelationIdGetRelation: %6d/%6d\n",
|
||||
stats->local_RelationIdGetRelation,
|
||||
stats->global_RelationIdGetRelation);
|
||||
|
||||
printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n",
|
||||
stats->local_RelationIdGetRelation_Buf,
|
||||
stats->global_RelationIdGetRelation_Buf);
|
||||
|
||||
printf("local/global_getreldesc: %6d/%6d\n",
|
||||
stats->local_getreldesc, stats->global_getreldesc);
|
||||
|
||||
printf("local/global_heapgettup: %6d/%6d\n",
|
||||
stats->local_heapgettup, stats->global_heapgettup);
|
||||
|
||||
printf("local/global_RelationPutHeapTuple: %6d/%6d\n",
|
||||
stats->local_RelationPutHeapTuple,
|
||||
stats->global_RelationPutHeapTuple);
|
||||
|
||||
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
|
||||
stats->local_RelationPutLongHeapTuple,
|
||||
stats->global_RelationPutLongHeapTuple);
|
||||
|
||||
printf("===================================\n");
|
||||
|
||||
printf("\n");
|
||||
/* ----------------
|
||||
* return nothing if stats aren't valid
|
||||
* ----------------
|
||||
*/
|
||||
if (stats == NULL)
|
||||
return;
|
||||
|
||||
printf("======== heap am statistics ========\n");
|
||||
printf("init_global_timestamp: %s",
|
||||
ctime(&(stats->init_global_timestamp)));
|
||||
|
||||
printf("local_reset_timestamp: %s",
|
||||
ctime(&(stats->local_reset_timestamp)));
|
||||
|
||||
printf("last_request_timestamp: %s",
|
||||
ctime(&(stats->last_request_timestamp)));
|
||||
|
||||
printf("local/global_open: %6d/%6d\n",
|
||||
stats->local_open, stats->global_open);
|
||||
|
||||
printf("local/global_openr: %6d/%6d\n",
|
||||
stats->local_openr, stats->global_openr);
|
||||
|
||||
printf("local/global_close: %6d/%6d\n",
|
||||
stats->local_close, stats->global_close);
|
||||
|
||||
printf("local/global_beginscan: %6d/%6d\n",
|
||||
stats->local_beginscan, stats->global_beginscan);
|
||||
|
||||
printf("local/global_rescan: %6d/%6d\n",
|
||||
stats->local_rescan, stats->global_rescan);
|
||||
|
||||
printf("local/global_endscan: %6d/%6d\n",
|
||||
stats->local_endscan, stats->global_endscan);
|
||||
|
||||
printf("local/global_getnext: %6d/%6d\n",
|
||||
stats->local_getnext, stats->global_getnext);
|
||||
|
||||
printf("local/global_fetch: %6d/%6d\n",
|
||||
stats->local_fetch, stats->global_fetch);
|
||||
|
||||
printf("local/global_insert: %6d/%6d\n",
|
||||
stats->local_insert, stats->global_insert);
|
||||
|
||||
printf("local/global_delete: %6d/%6d\n",
|
||||
stats->local_delete, stats->global_delete);
|
||||
|
||||
printf("local/global_replace: %6d/%6d\n",
|
||||
stats->local_replace, stats->global_replace);
|
||||
|
||||
printf("local/global_markpos: %6d/%6d\n",
|
||||
stats->local_markpos, stats->global_markpos);
|
||||
|
||||
printf("local/global_restrpos: %6d/%6d\n",
|
||||
stats->local_restrpos, stats->global_restrpos);
|
||||
|
||||
printf("================\n");
|
||||
|
||||
printf("local/global_BufferGetRelation: %6d/%6d\n",
|
||||
stats->local_BufferGetRelation,
|
||||
stats->global_BufferGetRelation);
|
||||
|
||||
printf("local/global_RelationIdGetRelation: %6d/%6d\n",
|
||||
stats->local_RelationIdGetRelation,
|
||||
stats->global_RelationIdGetRelation);
|
||||
|
||||
printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n",
|
||||
stats->local_RelationIdGetRelation_Buf,
|
||||
stats->global_RelationIdGetRelation_Buf);
|
||||
|
||||
printf("local/global_getreldesc: %6d/%6d\n",
|
||||
stats->local_getreldesc, stats->global_getreldesc);
|
||||
|
||||
printf("local/global_heapgettup: %6d/%6d\n",
|
||||
stats->local_heapgettup, stats->global_heapgettup);
|
||||
|
||||
printf("local/global_RelationPutHeapTuple: %6d/%6d\n",
|
||||
stats->local_RelationPutHeapTuple,
|
||||
stats->global_RelationPutHeapTuple);
|
||||
|
||||
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
|
||||
stats->local_RelationPutLongHeapTuple,
|
||||
stats->global_RelationPutLongHeapTuple);
|
||||
|
||||
printf("===================================\n");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* PrintAndFreeHeapAccessStatistics
|
||||
* PrintAndFreeHeapAccessStatistics
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats)
|
||||
{
|
||||
PrintHeapAccessStatistics(stats);
|
||||
if (stats != NULL)
|
||||
pfree(stats);
|
||||
PrintHeapAccessStatistics(stats);
|
||||
if (stats != NULL)
|
||||
pfree(stats);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* access method initialization
|
||||
* access method initialization
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------
|
||||
* initam should someday be moved someplace else.
|
||||
* initam should someday be moved someplace else.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
initam(void)
|
||||
{
|
||||
/* ----------------
|
||||
* initialize heap statistics.
|
||||
* ----------------
|
||||
*/
|
||||
InitHeapAccessStatistics();
|
||||
/* ----------------
|
||||
* initialize heap statistics.
|
||||
* ----------------
|
||||
*/
|
||||
InitHeapAccessStatistics();
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* genam.c--
|
||||
* general index access method routines
|
||||
* general index access method routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.7 1997/08/19 21:29:26 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.8 1997/09/07 04:38:17 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* many of the old access method routines have been turned into
|
||||
* macros and moved to genam.h -cim 4/30/91
|
||||
* many of the old access method routines have been turned into
|
||||
* macros and moved to genam.h -cim 4/30/91
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -29,18 +29,18 @@
|
||||
* previous, current, next. Note that the case of reverse scans works
|
||||
* identically.
|
||||
*
|
||||
* State Result
|
||||
* (1) + + - + 0 0 (if the next item pointer is invalid)
|
||||
* (2) + X - (otherwise)
|
||||
* (3) * 0 0 * 0 0 (no change)
|
||||
* (4) + X 0 X 0 0 (shift)
|
||||
* (5) * + X + X - (shift, add unknown)
|
||||
* State Result
|
||||
* (1) + + - + 0 0 (if the next item pointer is invalid)
|
||||
* (2) + X - (otherwise)
|
||||
* (3) * 0 0 * 0 0 (no change)
|
||||
* (4) + X 0 X 0 0 (shift)
|
||||
* (5) * + X + X - (shift, add unknown)
|
||||
*
|
||||
* All other states cannot occur.
|
||||
*
|
||||
* Note:
|
||||
*It would be possible to cache the status of the previous and
|
||||
* next item pointer using the flags.
|
||||
* next item pointer using the flags.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -51,220 +51,234 @@
|
||||
#include <storage/bufmgr.h>
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* general access method routines
|
||||
* general access method routines
|
||||
*
|
||||
* All indexed access methods use an identical scan structure.
|
||||
* We don't know how the various AMs do locking, however, so we don't
|
||||
* do anything about that here.
|
||||
* All indexed access methods use an identical scan structure.
|
||||
* We don't know how the various AMs do locking, however, so we don't
|
||||
* do anything about that here.
|
||||
*
|
||||
* The intent is that an AM implementor will define a front-end routine
|
||||
* that calls this one, to fill in the scan, and then does whatever kind
|
||||
* of locking he wants.
|
||||
* The intent is that an AM implementor will define a front-end routine
|
||||
* that calls this one, to fill in the scan, and then does whatever kind
|
||||
* of locking he wants.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* RelationGetIndexScan -- Create and fill an IndexScanDesc.
|
||||
* RelationGetIndexScan -- Create and fill an IndexScanDesc.
|
||||
*
|
||||
* This routine creates an index scan structure and sets its contents
|
||||
* up correctly. This routine calls AMrescan to set up the scan with
|
||||
* the passed key.
|
||||
* This routine creates an index scan structure and sets its contents
|
||||
* up correctly. This routine calls AMrescan to set up the scan with
|
||||
* the passed key.
|
||||
*
|
||||
* Parameters:
|
||||
* relation -- index relation for scan.
|
||||
* scanFromEnd -- if true, begin scan at one of the index's
|
||||
* endpoints.
|
||||
* numberOfKeys -- count of scan keys (more than one won't
|
||||
* necessarily do anything useful, yet).
|
||||
* key -- the ScanKey for the starting position of the scan.
|
||||
* Parameters:
|
||||
* relation -- index relation for scan.
|
||||
* scanFromEnd -- if true, begin scan at one of the index's
|
||||
* endpoints.
|
||||
* numberOfKeys -- count of scan keys (more than one won't
|
||||
* necessarily do anything useful, yet).
|
||||
* key -- the ScanKey for the starting position of the scan.
|
||||
*
|
||||
* Returns:
|
||||
* An initialized IndexScanDesc.
|
||||
* Returns:
|
||||
* An initialized IndexScanDesc.
|
||||
*
|
||||
* Side Effects:
|
||||
* Bumps the ref count on the relation to keep it in the cache.
|
||||
*
|
||||
* Side Effects:
|
||||
* Bumps the ref count on the relation to keep it in the cache.
|
||||
*
|
||||
* ----------------
|
||||
*/
|
||||
IndexScanDesc
|
||||
RelationGetIndexScan(Relation relation,
|
||||
bool scanFromEnd,
|
||||
uint16 numberOfKeys,
|
||||
ScanKey key)
|
||||
bool scanFromEnd,
|
||||
uint16 numberOfKeys,
|
||||
ScanKey key)
|
||||
{
|
||||
IndexScanDesc scan;
|
||||
|
||||
if (! RelationIsValid(relation))
|
||||
elog(WARN, "RelationGetIndexScan: relation invalid");
|
||||
|
||||
scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
|
||||
|
||||
scan->relation = relation;
|
||||
scan->opaque = NULL;
|
||||
scan->numberOfKeys = numberOfKeys;
|
||||
|
||||
ItemPointerSetInvalid(&scan->previousItemData);
|
||||
ItemPointerSetInvalid(&scan->currentItemData);
|
||||
ItemPointerSetInvalid(&scan->nextItemData);
|
||||
ItemPointerSetInvalid(&scan->previousMarkData);
|
||||
ItemPointerSetInvalid(&scan->currentMarkData);
|
||||
ItemPointerSetInvalid(&scan->nextMarkData);
|
||||
IndexScanDesc scan;
|
||||
|
||||
if (numberOfKeys > 0) {
|
||||
scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
|
||||
} else {
|
||||
scan->keyData = NULL;
|
||||
}
|
||||
if (!RelationIsValid(relation))
|
||||
elog(WARN, "RelationGetIndexScan: relation invalid");
|
||||
|
||||
index_rescan(scan, scanFromEnd, key);
|
||||
|
||||
return (scan);
|
||||
scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
|
||||
|
||||
scan->relation = relation;
|
||||
scan->opaque = NULL;
|
||||
scan->numberOfKeys = numberOfKeys;
|
||||
|
||||
ItemPointerSetInvalid(&scan->previousItemData);
|
||||
ItemPointerSetInvalid(&scan->currentItemData);
|
||||
ItemPointerSetInvalid(&scan->nextItemData);
|
||||
ItemPointerSetInvalid(&scan->previousMarkData);
|
||||
ItemPointerSetInvalid(&scan->currentMarkData);
|
||||
ItemPointerSetInvalid(&scan->nextMarkData);
|
||||
|
||||
if (numberOfKeys > 0)
|
||||
{
|
||||
scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
|
||||
}
|
||||
else
|
||||
{
|
||||
scan->keyData = NULL;
|
||||
}
|
||||
|
||||
index_rescan(scan, scanFromEnd, key);
|
||||
|
||||
return (scan);
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* IndexScanRestart -- Restart an index scan.
|
||||
* IndexScanRestart -- Restart an index scan.
|
||||
*
|
||||
* This routine isn't used by any existing access method. It's
|
||||
* appropriate if relation level locks are what you want.
|
||||
* This routine isn't used by any existing access method. It's
|
||||
* appropriate if relation level locks are what you want.
|
||||
*
|
||||
* Returns:
|
||||
* None.
|
||||
* Returns:
|
||||
* None.
|
||||
*
|
||||
* Side Effects:
|
||||
* None.
|
||||
* Side Effects:
|
||||
* None.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
IndexScanRestart(IndexScanDesc scan,
|
||||
bool scanFromEnd,
|
||||
ScanKey key)
|
||||
bool scanFromEnd,
|
||||
ScanKey key)
|
||||
{
|
||||
if (! IndexScanIsValid(scan))
|
||||
elog(WARN, "IndexScanRestart: invalid scan");
|
||||
|
||||
ItemPointerSetInvalid(&scan->previousItemData);
|
||||
ItemPointerSetInvalid(&scan->currentItemData);
|
||||
ItemPointerSetInvalid(&scan->nextItemData);
|
||||
|
||||
if (RelationGetNumberOfBlocks(scan->relation) == 0)
|
||||
scan->flags = ScanUnmarked;
|
||||
else if (scanFromEnd)
|
||||
scan->flags = ScanUnmarked | ScanUncheckedPrevious;
|
||||
else
|
||||
scan->flags = ScanUnmarked | ScanUncheckedNext;
|
||||
|
||||
scan->scanFromEnd = (bool) scanFromEnd;
|
||||
|
||||
if (scan->numberOfKeys > 0)
|
||||
memmove(scan->keyData,
|
||||
key,
|
||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||
if (!IndexScanIsValid(scan))
|
||||
elog(WARN, "IndexScanRestart: invalid scan");
|
||||
|
||||
ItemPointerSetInvalid(&scan->previousItemData);
|
||||
ItemPointerSetInvalid(&scan->currentItemData);
|
||||
ItemPointerSetInvalid(&scan->nextItemData);
|
||||
|
||||
if (RelationGetNumberOfBlocks(scan->relation) == 0)
|
||||
scan->flags = ScanUnmarked;
|
||||
else if (scanFromEnd)
|
||||
scan->flags = ScanUnmarked | ScanUncheckedPrevious;
|
||||
else
|
||||
scan->flags = ScanUnmarked | ScanUncheckedNext;
|
||||
|
||||
scan->scanFromEnd = (bool) scanFromEnd;
|
||||
|
||||
if (scan->numberOfKeys > 0)
|
||||
memmove(scan->keyData,
|
||||
key,
|
||||
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* IndexScanEnd -- End and index scan.
|
||||
* IndexScanEnd -- End and index scan.
|
||||
*
|
||||
* This routine is not used by any existing access method, but is
|
||||
* suitable for use if you don't want to do sophisticated locking.
|
||||
* This routine is not used by any existing access method, but is
|
||||
* suitable for use if you don't want to do sophisticated locking.
|
||||
*
|
||||
* Returns:
|
||||
* None.
|
||||
* Returns:
|
||||
* None.
|
||||
*
|
||||
* Side Effects:
|
||||
* None.
|
||||
* Side Effects:
|
||||
* None.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
IndexScanEnd(IndexScanDesc scan)
|
||||
{
|
||||
if (! IndexScanIsValid(scan))
|
||||
elog(WARN, "IndexScanEnd: invalid scan");
|
||||
|
||||
pfree(scan);
|
||||
if (!IndexScanIsValid(scan))
|
||||
elog(WARN, "IndexScanEnd: invalid scan");
|
||||
|
||||
pfree(scan);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* IndexScanMarkPosition -- Mark current position in a scan.
|
||||
* IndexScanMarkPosition -- Mark current position in a scan.
|
||||
*
|
||||
* This routine isn't used by any existing access method, but is the
|
||||
* one that AM implementors should use, if they don't want to do any
|
||||
* special locking. If relation-level locking is sufficient, this is
|
||||
* the routine for you.
|
||||
* This routine isn't used by any existing access method, but is the
|
||||
* one that AM implementors should use, if they don't want to do any
|
||||
* special locking. If relation-level locking is sufficient, this is
|
||||
* the routine for you.
|
||||
*
|
||||
* Returns:
|
||||
* None.
|
||||
* Returns:
|
||||
* None.
|
||||
*
|
||||
* Side Effects:
|
||||
* None.
|
||||
* Side Effects:
|
||||
* None.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
IndexScanMarkPosition(IndexScanDesc scan)
|
||||
{
|
||||
RetrieveIndexResult result;
|
||||
|
||||
if (scan->flags & ScanUncheckedPrevious) {
|
||||
result =
|
||||
index_getnext(scan, BackwardScanDirection);
|
||||
|
||||
if (result != NULL) {
|
||||
scan->previousItemData = result->index_iptr;
|
||||
} else {
|
||||
ItemPointerSetInvalid(&scan->previousItemData);
|
||||
RetrieveIndexResult result;
|
||||
|
||||
if (scan->flags & ScanUncheckedPrevious)
|
||||
{
|
||||
result =
|
||||
index_getnext(scan, BackwardScanDirection);
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
scan->previousItemData = result->index_iptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPointerSetInvalid(&scan->previousItemData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (scan->flags & ScanUncheckedNext) {
|
||||
result = (RetrieveIndexResult)
|
||||
index_getnext(scan, ForwardScanDirection);
|
||||
|
||||
if (result != NULL) {
|
||||
scan->nextItemData = result->index_iptr;
|
||||
} else {
|
||||
ItemPointerSetInvalid(&scan->nextItemData);
|
||||
else if (scan->flags & ScanUncheckedNext)
|
||||
{
|
||||
result = (RetrieveIndexResult)
|
||||
index_getnext(scan, ForwardScanDirection);
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
scan->nextItemData = result->index_iptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPointerSetInvalid(&scan->nextItemData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan->previousMarkData = scan->previousItemData;
|
||||
scan->currentMarkData = scan->currentItemData;
|
||||
scan->nextMarkData = scan->nextItemData;
|
||||
|
||||
scan->flags = 0x0; /* XXX should have a symbolic name */
|
||||
|
||||
scan->previousMarkData = scan->previousItemData;
|
||||
scan->currentMarkData = scan->currentItemData;
|
||||
scan->nextMarkData = scan->nextItemData;
|
||||
|
||||
scan->flags = 0x0; /* XXX should have a symbolic name */
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* IndexScanRestorePosition -- Restore position on a marked scan.
|
||||
* IndexScanRestorePosition -- Restore position on a marked scan.
|
||||
*
|
||||
* This routine isn't used by any existing access method, but is the
|
||||
* one that AM implementors should use if they don't want to do any
|
||||
* special locking. If relation-level locking is sufficient, then
|
||||
* this is the one you want.
|
||||
* This routine isn't used by any existing access method, but is the
|
||||
* one that AM implementors should use if they don't want to do any
|
||||
* special locking. If relation-level locking is sufficient, then
|
||||
* this is the one you want.
|
||||
*
|
||||
* Returns:
|
||||
* None.
|
||||
* Returns:
|
||||
* None.
|
||||
*
|
||||
* Side Effects:
|
||||
* None.
|
||||
* Side Effects:
|
||||
* None.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
IndexScanRestorePosition(IndexScanDesc scan)
|
||||
{
|
||||
if (scan->flags & ScanUnmarked)
|
||||
elog(WARN, "IndexScanRestorePosition: no mark to restore");
|
||||
|
||||
scan->previousItemData = scan->previousMarkData;
|
||||
scan->currentItemData = scan->currentMarkData;
|
||||
scan->nextItemData = scan->nextMarkData;
|
||||
|
||||
scan->flags = 0x0; /* XXX should have a symbolic name */
|
||||
{
|
||||
if (scan->flags & ScanUnmarked)
|
||||
elog(WARN, "IndexScanRestorePosition: no mark to restore");
|
||||
|
||||
scan->previousItemData = scan->previousMarkData;
|
||||
scan->currentItemData = scan->currentMarkData;
|
||||
scan->nextItemData = scan->nextMarkData;
|
||||
|
||||
scan->flags = 0x0; /* XXX should have a symbolic name */
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indexam.c--
|
||||
* general index access method routines
|
||||
* general index access method routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.13 1997/08/26 23:31:28 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.14 1997/09/07 04:38:26 momjian Exp $
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
* index_open - open an index relation by relationId
|
||||
* index_openr - open a index relation by name
|
||||
* index_close - close a index relation
|
||||
* index_beginscan - start a scan of an index
|
||||
* index_rescan - restart a scan of an index
|
||||
* index_endscan - end a scan
|
||||
* index_insert - insert an index tuple into a relation
|
||||
* index_delete - delete an item from an index relation
|
||||
* index_markpos - mark a scan position
|
||||
* index_restrpos - restore a scan position
|
||||
* index_getnext - get the next tuple from a scan
|
||||
* ** index_fetch - retrieve tuple with tid
|
||||
* index_open - open an index relation by relationId
|
||||
* index_openr - open a index relation by name
|
||||
* index_close - close a index relation
|
||||
* index_beginscan - start a scan of an index
|
||||
* index_rescan - restart a scan of an index
|
||||
* index_endscan - end a scan
|
||||
* index_insert - insert an index tuple into a relation
|
||||
* index_delete - delete an item from an index relation
|
||||
* index_markpos - mark a scan position
|
||||
* index_restrpos - restore a scan position
|
||||
* index_getnext - get the next tuple from a scan
|
||||
* ** index_fetch - retrieve tuple with tid
|
||||
* ** index_replace - replace a tuple
|
||||
* ** index_getattr - get an attribute from an index tuple
|
||||
* index_getprocid - get a support procedure id from the rel tuple
|
||||
*
|
||||
* IndexScanIsValid - check index scan
|
||||
* index_getprocid - get a support procedure id from the rel tuple
|
||||
*
|
||||
* IndexScanIsValid - check index scan
|
||||
*
|
||||
* NOTES
|
||||
* This file contains the index_ routines which used
|
||||
* to be a scattered collection of stuff in access/genam.
|
||||
* This file contains the index_ routines which used
|
||||
* to be a scattered collection of stuff in access/genam.
|
||||
*
|
||||
* The ** routines: index_fetch, index_replace, and index_getattr
|
||||
* have not yet been implemented. They may not be needed.
|
||||
* The ** routines: index_fetch, index_replace, and index_getattr
|
||||
* have not yet been implemented. They may not be needed.
|
||||
*
|
||||
* old comments
|
||||
* Scans are implemented as follows:
|
||||
* Scans are implemented as follows:
|
||||
*
|
||||
* `0' represents an invalid item pointer.
|
||||
* `-' represents an unknown item pointer.
|
||||
* `X' represents a known item pointers.
|
||||
* `+' represents known or invalid item pointers.
|
||||
* `*' represents any item pointers.
|
||||
* `0' represents an invalid item pointer.
|
||||
* `-' represents an unknown item pointer.
|
||||
* `X' represents a known item pointers.
|
||||
* `+' represents known or invalid item pointers.
|
||||
* `*' represents any item pointers.
|
||||
*
|
||||
* State is represented by a triple of these symbols in the order of
|
||||
* previous, current, next. Note that the case of reverse scans works
|
||||
* identically.
|
||||
* State is represented by a triple of these symbols in the order of
|
||||
* previous, current, next. Note that the case of reverse scans works
|
||||
* identically.
|
||||
*
|
||||
* State Result
|
||||
* (1) + + - + 0 0 (if the next item pointer is invalid)
|
||||
* (2) + X - (otherwise)
|
||||
* (3) * 0 0 * 0 0 (no change)
|
||||
* (4) + X 0 X 0 0 (shift)
|
||||
* (5) * + X + X - (shift, add unknown)
|
||||
* State Result
|
||||
* (1) + + - + 0 0 (if the next item pointer is invalid)
|
||||
* (2) + X - (otherwise)
|
||||
* (3) * 0 0 * 0 0 (no change)
|
||||
* (4) + X 0 X 0 0 (shift)
|
||||
* (5) * + X + X - (shift, add unknown)
|
||||
*
|
||||
* All other states cannot occur.
|
||||
* All other states cannot occur.
|
||||
*
|
||||
* Note: It would be possible to cache the status of the previous and
|
||||
* next item pointer using the flags.
|
||||
* Note: It would be possible to cache the status of the previous and
|
||||
* next item pointer using the flags.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
#include <access/genam.h>
|
||||
|
||||
#include <access/genam.h>
|
||||
#include <utils/relcache.h>
|
||||
#include <fmgr.h>
|
||||
#include <storage/lmgr.h>
|
||||
#include <access/heapam.h>
|
||||
|
||||
/* ----------------
|
||||
* undefine macros we aren't going to use that would otherwise
|
||||
* get in our way.. delete is defined in c.h and the am's are
|
||||
* defined in heapam.h
|
||||
* undefine macros we aren't going to use that would otherwise
|
||||
* get in our way.. delete is defined in c.h and the am's are
|
||||
* defined in heapam.h
|
||||
* ----------------
|
||||
*/
|
||||
#undef delete
|
||||
@@ -88,314 +88,320 @@
|
||||
#undef amgettuple
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* macros used in index_ routines
|
||||
* macros used in index_ routines
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#define RELATION_CHECKS \
|
||||
Assert(RelationIsValid(relation)); \
|
||||
Assert(PointerIsValid(relation->rd_am))
|
||||
|
||||
Assert(PointerIsValid(relation->rd_am))
|
||||
|
||||
#define SCAN_CHECKS \
|
||||
Assert(IndexScanIsValid(scan)); \
|
||||
Assert(RelationIsValid(scan->relation)); \
|
||||
Assert(PointerIsValid(scan->relation->rd_am))
|
||||
|
||||
Assert(IndexScanIsValid(scan)); \
|
||||
Assert(RelationIsValid(scan->relation)); \
|
||||
Assert(PointerIsValid(scan->relation->rd_am))
|
||||
|
||||
#define GET_REL_PROCEDURE(x,y) \
|
||||
procedure = relation->rd_am->y; \
|
||||
if (! RegProcedureIsValid(procedure)) \
|
||||
elog(WARN, "index_%s: invalid %s regproc", \
|
||||
CppAsString(x), CppAsString(y))
|
||||
|
||||
procedure = relation->rd_am->y; \
|
||||
if (! RegProcedureIsValid(procedure)) \
|
||||
elog(WARN, "index_%s: invalid %s regproc", \
|
||||
CppAsString(x), CppAsString(y))
|
||||
|
||||
#define GET_SCAN_PROCEDURE(x,y) \
|
||||
procedure = scan->relation->rd_am->y; \
|
||||
if (! RegProcedureIsValid(procedure)) \
|
||||
elog(WARN, "index_%s: invalid %s regproc", \
|
||||
CppAsString(x), CppAsString(y))
|
||||
|
||||
|
||||
procedure = scan->relation->rd_am->y; \
|
||||
if (! RegProcedureIsValid(procedure)) \
|
||||
elog(WARN, "index_%s: invalid %s regproc", \
|
||||
CppAsString(x), CppAsString(y))
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* index_ interface functions
|
||||
* index_ interface functions
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------
|
||||
* index_open - open an index relation by relationId
|
||||
* index_open - open an index relation by relationId
|
||||
*
|
||||
* presently the relcache routines do all the work we need
|
||||
* to open/close index relations.
|
||||
* presently the relcache routines do all the work we need
|
||||
* to open/close index relations.
|
||||
* ----------------
|
||||
*/
|
||||
Relation
|
||||
index_open(Oid relationId)
|
||||
{
|
||||
return RelationIdGetRelation(relationId);
|
||||
return RelationIdGetRelation(relationId);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_openr - open a index relation by name
|
||||
* index_openr - open a index relation by name
|
||||
*
|
||||
* presently the relcache routines do all the work we need
|
||||
* to open/close index relations.
|
||||
* presently the relcache routines do all the work we need
|
||||
* to open/close index relations.
|
||||
* ----------------
|
||||
*/
|
||||
Relation
|
||||
index_openr(char *relationName)
|
||||
{
|
||||
return RelationNameGetRelation(relationName);
|
||||
return RelationNameGetRelation(relationName);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_close - close a index relation
|
||||
* index_close - close a index relation
|
||||
*
|
||||
* presently the relcache routines do all the work we need
|
||||
* to open/close index relations.
|
||||
* presently the relcache routines do all the work we need
|
||||
* to open/close index relations.
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
index_close(Relation relation)
|
||||
{
|
||||
RelationClose(relation);
|
||||
RelationClose(relation);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_insert - insert an index tuple into a relation
|
||||
* index_insert - insert an index tuple into a relation
|
||||
* ----------------
|
||||
*/
|
||||
InsertIndexResult
|
||||
index_insert(Relation relation,
|
||||
Datum *datum,
|
||||
char *nulls,
|
||||
ItemPointer heap_t_ctid,
|
||||
Relation heapRel)
|
||||
Datum * datum,
|
||||
char *nulls,
|
||||
ItemPointer heap_t_ctid,
|
||||
Relation heapRel)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
InsertIndexResult specificResult;
|
||||
|
||||
RELATION_CHECKS;
|
||||
GET_REL_PROCEDURE(insert,aminsert);
|
||||
|
||||
/* ----------------
|
||||
* have the am's insert proc do all the work.
|
||||
* ----------------
|
||||
*/
|
||||
specificResult = (InsertIndexResult)
|
||||
fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL);
|
||||
|
||||
/* ----------------
|
||||
* the insert proc is supposed to return a "specific result" and
|
||||
* this routine has to return a "general result" so after we get
|
||||
* something back from the insert proc, we allocate a
|
||||
* "general result" and copy some crap between the two.
|
||||
*
|
||||
* As far as I'm concerned all this result shit is needlessly c
|
||||
* omplicated and should be eliminated. -cim 1/19/91
|
||||
*
|
||||
* mao concurs. regardless of how we feel here, however, it is
|
||||
* important to free memory we don't intend to return to anyone.
|
||||
* 2/28/91
|
||||
*
|
||||
* this "general result" crap is now gone. -ay 3/6/95
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
return (specificResult);
|
||||
RegProcedure procedure;
|
||||
InsertIndexResult specificResult;
|
||||
|
||||
RELATION_CHECKS;
|
||||
GET_REL_PROCEDURE(insert, aminsert);
|
||||
|
||||
/* ----------------
|
||||
* have the am's insert proc do all the work.
|
||||
* ----------------
|
||||
*/
|
||||
specificResult = (InsertIndexResult)
|
||||
fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL);
|
||||
|
||||
/* ----------------
|
||||
* the insert proc is supposed to return a "specific result" and
|
||||
* this routine has to return a "general result" so after we get
|
||||
* something back from the insert proc, we allocate a
|
||||
* "general result" and copy some crap between the two.
|
||||
*
|
||||
* As far as I'm concerned all this result shit is needlessly c
|
||||
* omplicated and should be eliminated. -cim 1/19/91
|
||||
*
|
||||
* mao concurs. regardless of how we feel here, however, it is
|
||||
* important to free memory we don't intend to return to anyone.
|
||||
* 2/28/91
|
||||
*
|
||||
* this "general result" crap is now gone. -ay 3/6/95
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
return (specificResult);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_delete - delete an item from an index relation
|
||||
* index_delete - delete an item from an index relation
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
index_delete(Relation relation, ItemPointer indexItem)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
|
||||
RELATION_CHECKS;
|
||||
GET_REL_PROCEDURE(delete,amdelete);
|
||||
|
||||
fmgr(procedure, relation, indexItem);
|
||||
RegProcedure procedure;
|
||||
|
||||
RELATION_CHECKS;
|
||||
GET_REL_PROCEDURE(delete, amdelete);
|
||||
|
||||
fmgr(procedure, relation, indexItem);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_beginscan - start a scan of an index
|
||||
* index_beginscan - start a scan of an index
|
||||
* ----------------
|
||||
*/
|
||||
IndexScanDesc
|
||||
index_beginscan(Relation relation,
|
||||
bool scanFromEnd,
|
||||
uint16 numberOfKeys,
|
||||
ScanKey key)
|
||||
bool scanFromEnd,
|
||||
uint16 numberOfKeys,
|
||||
ScanKey key)
|
||||
{
|
||||
IndexScanDesc scandesc;
|
||||
RegProcedure procedure;
|
||||
|
||||
RELATION_CHECKS;
|
||||
GET_REL_PROCEDURE(beginscan,ambeginscan);
|
||||
|
||||
RelationSetRIntentLock(relation);
|
||||
|
||||
scandesc = (IndexScanDesc)
|
||||
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
|
||||
|
||||
return scandesc;
|
||||
IndexScanDesc scandesc;
|
||||
RegProcedure procedure;
|
||||
|
||||
RELATION_CHECKS;
|
||||
GET_REL_PROCEDURE(beginscan, ambeginscan);
|
||||
|
||||
RelationSetRIntentLock(relation);
|
||||
|
||||
scandesc = (IndexScanDesc)
|
||||
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
|
||||
|
||||
return scandesc;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_rescan - restart a scan of an index
|
||||
* index_rescan - restart a scan of an index
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(rescan,amrescan);
|
||||
|
||||
fmgr(procedure, scan, scanFromEnd, key);
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(rescan, amrescan);
|
||||
|
||||
fmgr(procedure, scan, scanFromEnd, key);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_endscan - end a scan
|
||||
* index_endscan - end a scan
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
index_endscan(IndexScanDesc scan)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(endscan,amendscan);
|
||||
|
||||
fmgr(procedure, scan);
|
||||
|
||||
RelationUnsetRIntentLock(scan->relation);
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(endscan, amendscan);
|
||||
|
||||
fmgr(procedure, scan);
|
||||
|
||||
RelationUnsetRIntentLock(scan->relation);
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* index_markpos - mark a scan position
|
||||
* index_markpos - mark a scan position
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
index_markpos(IndexScanDesc scan)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(markpos,ammarkpos);
|
||||
|
||||
fmgr(procedure, scan);
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(markpos, ammarkpos);
|
||||
|
||||
fmgr(procedure, scan);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* index_restrpos - restore a scan position
|
||||
* index_restrpos - restore a scan position
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
index_restrpos(IndexScanDesc scan)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(restrpos,amrestrpos);
|
||||
|
||||
fmgr(procedure, scan);
|
||||
RegProcedure procedure;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(restrpos, amrestrpos);
|
||||
|
||||
fmgr(procedure, scan);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* index_getnext - get the next tuple from a scan
|
||||
* index_getnext - get the next tuple from a scan
|
||||
*
|
||||
* A RetrieveIndexResult is a index tuple/heap tuple pair
|
||||
* A RetrieveIndexResult is a index tuple/heap tuple pair
|
||||
* ----------------
|
||||
*/
|
||||
RetrieveIndexResult
|
||||
index_getnext(IndexScanDesc scan,
|
||||
ScanDirection direction)
|
||||
ScanDirection direction)
|
||||
{
|
||||
RegProcedure procedure;
|
||||
RetrieveIndexResult result;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(getnext,amgettuple);
|
||||
|
||||
/* ----------------
|
||||
* have the am's gettuple proc do all the work.
|
||||
* ----------------
|
||||
*/
|
||||
result = (RetrieveIndexResult)
|
||||
fmgr(procedure, scan, direction);
|
||||
|
||||
return result;
|
||||
RegProcedure procedure;
|
||||
RetrieveIndexResult result;
|
||||
|
||||
SCAN_CHECKS;
|
||||
GET_SCAN_PROCEDURE(getnext, amgettuple);
|
||||
|
||||
/* ----------------
|
||||
* have the am's gettuple proc do all the work.
|
||||
* ----------------
|
||||
*/
|
||||
result = (RetrieveIndexResult)
|
||||
fmgr(procedure, scan, direction);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* index_getprocid
|
||||
* index_getprocid
|
||||
*
|
||||
* Some indexed access methods may require support routines that are
|
||||
* not in the operator class/operator model imposed by pg_am. These
|
||||
* access methods may store the OIDs of registered procedures they
|
||||
* need in pg_amproc. These registered procedure OIDs are ordered in
|
||||
* a way that makes sense to the access method, and used only by the
|
||||
* access method. The general index code doesn't know anything about
|
||||
* the routines involved; it just builds an ordered list of them for
|
||||
* each attribute on which an index is defined.
|
||||
* Some indexed access methods may require support routines that are
|
||||
* not in the operator class/operator model imposed by pg_am. These
|
||||
* access methods may store the OIDs of registered procedures they
|
||||
* need in pg_amproc. These registered procedure OIDs are ordered in
|
||||
* a way that makes sense to the access method, and used only by the
|
||||
* access method. The general index code doesn't know anything about
|
||||
* the routines involved; it just builds an ordered list of them for
|
||||
* each attribute on which an index is defined.
|
||||
*
|
||||
* This routine returns the requested procedure OID for a particular
|
||||
* indexed attribute.
|
||||
* This routine returns the requested procedure OID for a particular
|
||||
* indexed attribute.
|
||||
* ----------------
|
||||
*/
|
||||
RegProcedure
|
||||
index_getprocid(Relation irel,
|
||||
AttrNumber attnum,
|
||||
uint16 procnum)
|
||||
AttrNumber attnum,
|
||||
uint16 procnum)
|
||||
{
|
||||
RegProcedure *loc;
|
||||
int natts;
|
||||
|
||||
natts = irel->rd_rel->relnatts;
|
||||
|
||||
loc = irel->rd_support;
|
||||
RegProcedure *loc;
|
||||
int natts;
|
||||
|
||||
Assert(loc != NULL);
|
||||
|
||||
return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
|
||||
natts = irel->rd_rel->relnatts;
|
||||
|
||||
loc = irel->rd_support;
|
||||
|
||||
Assert(loc != NULL);
|
||||
|
||||
return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
|
||||
}
|
||||
|
||||
Datum
|
||||
GetIndexValue(HeapTuple tuple,
|
||||
TupleDesc hTupDesc,
|
||||
int attOff,
|
||||
AttrNumber attrNums[],
|
||||
FuncIndexInfo *fInfo,
|
||||
bool *attNull,
|
||||
Buffer buffer)
|
||||
TupleDesc hTupDesc,
|
||||
int attOff,
|
||||
AttrNumber attrNums[],
|
||||
FuncIndexInfo * fInfo,
|
||||
bool * attNull,
|
||||
Buffer buffer)
|
||||
{
|
||||
Datum returnVal;
|
||||
bool isNull;
|
||||
|
||||
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) {
|
||||
int i;
|
||||
Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum));
|
||||
|
||||
for (i = 0; i < FIgetnArgs(fInfo); i++) {
|
||||
attData[i] = (Datum) heap_getattr(tuple,
|
||||
buffer,
|
||||
attrNums[i],
|
||||
hTupDesc,
|
||||
attNull);
|
||||
Datum returnVal;
|
||||
bool isNull;
|
||||
|
||||
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
|
||||
{
|
||||
int i;
|
||||
Datum *attData = (Datum *) palloc(FIgetnArgs(fInfo) * sizeof(Datum));
|
||||
|
||||
for (i = 0; i < FIgetnArgs(fInfo); i++)
|
||||
{
|
||||
attData[i] = (Datum) heap_getattr(tuple,
|
||||
buffer,
|
||||
attrNums[i],
|
||||
hTupDesc,
|
||||
attNull);
|
||||
}
|
||||
returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo),
|
||||
FIgetnArgs(fInfo),
|
||||
(char **) attData,
|
||||
&isNull);
|
||||
pfree(attData);
|
||||
*attNull = FALSE;
|
||||
}
|
||||
returnVal = (Datum)fmgr_array_args(FIgetProcOid(fInfo),
|
||||
FIgetnArgs(fInfo),
|
||||
(char **) attData,
|
||||
&isNull);
|
||||
pfree(attData);
|
||||
*attNull = FALSE;
|
||||
}else {
|
||||
returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff],
|
||||
hTupDesc, attNull);
|
||||
}
|
||||
return returnVal;
|
||||
else
|
||||
{
|
||||
returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff],
|
||||
hTupDesc, attNull);
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,22 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nbtcompare.c--
|
||||
* Comparison functions for btree access method.
|
||||
* Comparison functions for btree access method.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.10 1997/06/11 05:20:05 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.11 1997/09/07 04:38:39 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* These functions are stored in pg_amproc. For each operator class
|
||||
* defined on btrees, they compute
|
||||
* NOTES
|
||||
* These functions are stored in pg_amproc. For each operator class
|
||||
* defined on btrees, they compute
|
||||
*
|
||||
* compare(a, b):
|
||||
* < 0 if a < b,
|
||||
* = 0 if a == b,
|
||||
* > 0 if a > b.
|
||||
* compare(a, b):
|
||||
* < 0 if a < b,
|
||||
* = 0 if a == b,
|
||||
* > 0 if a > b.
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -30,168 +30,171 @@
|
||||
int32
|
||||
btint2cmp(int16 a, int16 b)
|
||||
{
|
||||
return ((int32) (a - b));
|
||||
return ((int32) (a - b));
|
||||
}
|
||||
|
||||
int32
|
||||
btint4cmp(int32 a, int32 b)
|
||||
{
|
||||
return (a - b);
|
||||
return (a - b);
|
||||
}
|
||||
|
||||
int32
|
||||
btint24cmp(int16 a, int32 b)
|
||||
{
|
||||
return (((int32) a) - b);
|
||||
return (((int32) a) - b);
|
||||
}
|
||||
|
||||
int32
|
||||
btint42cmp(int32 a, int16 b)
|
||||
{
|
||||
return (a - ((int32) b));
|
||||
return (a - ((int32) b));
|
||||
}
|
||||
|
||||
int32
|
||||
btfloat4cmp(float32 a, float32 b)
|
||||
{
|
||||
if (*a > *b)
|
||||
return (1);
|
||||
else if (*a == *b)
|
||||
return (0);
|
||||
else
|
||||
return (-1);
|
||||
if (*a > *b)
|
||||
return (1);
|
||||
else if (*a == *b)
|
||||
return (0);
|
||||
else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int32
|
||||
btfloat8cmp(float64 a, float64 b)
|
||||
{
|
||||
if (*a > *b)
|
||||
return (1);
|
||||
else if (*a == *b)
|
||||
return (0);
|
||||
else
|
||||
return (-1);
|
||||
if (*a > *b)
|
||||
return (1);
|
||||
else if (*a == *b)
|
||||
return (0);
|
||||
else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int32
|
||||
btoidcmp(Oid a, Oid b)
|
||||
{
|
||||
if (a > b)
|
||||
return (1);
|
||||
else if (a == b)
|
||||
return (0);
|
||||
else
|
||||
return (-1);
|
||||
if (a > b)
|
||||
return (1);
|
||||
else if (a == b)
|
||||
return (0);
|
||||
else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int32
|
||||
btabstimecmp(AbsoluteTime a, AbsoluteTime b)
|
||||
{
|
||||
if (AbsoluteTimeIsBefore(a, b))
|
||||
return (-1);
|
||||
else if (AbsoluteTimeIsBefore(b, a))
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
if (AbsoluteTimeIsBefore(a, b))
|
||||
return (-1);
|
||||
else if (AbsoluteTimeIsBefore(b, a))
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
int32
|
||||
btcharcmp(char a, char b)
|
||||
{
|
||||
return ((int32) ((uint8)a - (uint8)b));
|
||||
return ((int32) ((uint8) a - (uint8) b));
|
||||
}
|
||||
|
||||
int32
|
||||
btchar2cmp(uint16 a, uint16 b)
|
||||
{
|
||||
return (strncmp((char *) &a, (char *) &b, 2));
|
||||
return (strncmp((char *) &a, (char *) &b, 2));
|
||||
}
|
||||
|
||||
int32
|
||||
btchar4cmp(uint32 a, uint32 b)
|
||||
{
|
||||
return (strncmp((char *) &a, (char *) &b, 4));
|
||||
return (strncmp((char *) &a, (char *) &b, 4));
|
||||
}
|
||||
|
||||
int32
|
||||
btchar8cmp(char *a, char *b)
|
||||
{
|
||||
return (strncmp(a, b, 8));
|
||||
return (strncmp(a, b, 8));
|
||||
}
|
||||
|
||||
int32
|
||||
btchar16cmp(char *a, char *b)
|
||||
{
|
||||
return (strncmp(a, b, 16));
|
||||
return (strncmp(a, b, 16));
|
||||
}
|
||||
|
||||
int32
|
||||
btnamecmp(NameData *a, NameData *b)
|
||||
btnamecmp(NameData * a, NameData * b)
|
||||
{
|
||||
return (strncmp(a->data, b->data, NAMEDATALEN));
|
||||
return (strncmp(a->data, b->data, NAMEDATALEN));
|
||||
}
|
||||
|
||||
int32
|
||||
bttextcmp(struct varlena *a, struct varlena *b)
|
||||
bttextcmp(struct varlena * a, struct varlena * b)
|
||||
{
|
||||
int res;
|
||||
unsigned char *ap, *bp;
|
||||
int res;
|
||||
unsigned char *ap,
|
||||
*bp;
|
||||
|
||||
#ifdef USE_LOCALE
|
||||
int la = VARSIZE(a) - VARHDRSZ;
|
||||
int lb = VARSIZE(b) - VARHDRSZ;
|
||||
|
||||
ap = (unsigned char *) palloc (la + 1);
|
||||
bp = (unsigned char *) palloc (lb + 1);
|
||||
int la = VARSIZE(a) - VARHDRSZ;
|
||||
int lb = VARSIZE(b) - VARHDRSZ;
|
||||
|
||||
memcpy(ap, VARDATA(a), la);
|
||||
*(ap + la) = '\0';
|
||||
memcpy(bp, VARDATA(b), lb);
|
||||
*(bp + lb) = '\0';
|
||||
ap = (unsigned char *) palloc(la + 1);
|
||||
bp = (unsigned char *) palloc(lb + 1);
|
||||
|
||||
res = strcoll (ap, bp);
|
||||
|
||||
pfree (ap);
|
||||
pfree (bp);
|
||||
memcpy(ap, VARDATA(a), la);
|
||||
*(ap + la) = '\0';
|
||||
memcpy(bp, VARDATA(b), lb);
|
||||
*(bp + lb) = '\0';
|
||||
|
||||
res = strcoll(ap, bp);
|
||||
|
||||
pfree(ap);
|
||||
pfree(bp);
|
||||
|
||||
#else
|
||||
int len = VARSIZE(a);
|
||||
|
||||
/* len is the length of the shorter of the two strings */
|
||||
if ( len > VARSIZE(b) )
|
||||
len = VARSIZE(b);
|
||||
int len = VARSIZE(a);
|
||||
|
||||
len -= VARHDRSZ;
|
||||
/* len is the length of the shorter of the two strings */
|
||||
if (len > VARSIZE(b))
|
||||
len = VARSIZE(b);
|
||||
|
||||
ap = (unsigned char *) VARDATA(a);
|
||||
bp = (unsigned char *) VARDATA(b);
|
||||
|
||||
/*
|
||||
* If the two strings differ in the first len bytes, or if they're
|
||||
* the same in the first len bytes and they're both len bytes long,
|
||||
* we're done.
|
||||
*/
|
||||
|
||||
res = 0;
|
||||
if (len > 0) {
|
||||
do {
|
||||
res = (int) (*ap++ - *bp++);
|
||||
len--;
|
||||
} while (res == 0 && len != 0);
|
||||
}
|
||||
len -= VARHDRSZ;
|
||||
|
||||
ap = (unsigned char *) VARDATA(a);
|
||||
bp = (unsigned char *) VARDATA(b);
|
||||
|
||||
/*
|
||||
* If the two strings differ in the first len bytes, or if they're the
|
||||
* same in the first len bytes and they're both len bytes long, we're
|
||||
* done.
|
||||
*/
|
||||
|
||||
res = 0;
|
||||
if (len > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
res = (int) (*ap++ - *bp++);
|
||||
len--;
|
||||
} while (res == 0 && len != 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (res != 0 || VARSIZE(a) == VARSIZE(b))
|
||||
return (res);
|
||||
|
||||
/*
|
||||
* The two strings are the same in the first len bytes, and they
|
||||
* are of different lengths.
|
||||
*/
|
||||
|
||||
if (VARSIZE(a) < VARSIZE(b))
|
||||
return (-1);
|
||||
else
|
||||
return (1);
|
||||
|
||||
if (res != 0 || VARSIZE(a) == VARSIZE(b))
|
||||
return (res);
|
||||
|
||||
/*
|
||||
* The two strings are the same in the first len bytes, and they are
|
||||
* of different lengths.
|
||||
*/
|
||||
|
||||
if (VARSIZE(a) < VARSIZE(b))
|
||||
return (-1);
|
||||
else
|
||||
return (1);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,28 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* btscan.c--
|
||||
* manage scans on btrees.
|
||||
* manage scans on btrees.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.7 1997/02/18 17:13:45 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.8 1997/09/07 04:38:57 momjian Exp $
|
||||
*
|
||||
*
|
||||
* NOTES
|
||||
* Because we can be doing an index scan on a relation while we update
|
||||
* it, we need to avoid missing data that moves around in the index.
|
||||
* The routines and global variables in this file guarantee that all
|
||||
* scans in the local address space stay correctly positioned. This
|
||||
* is all we need to worry about, since write locking guarantees that
|
||||
* no one else will be on the same page at the same time as we are.
|
||||
* Because we can be doing an index scan on a relation while we update
|
||||
* it, we need to avoid missing data that moves around in the index.
|
||||
* The routines and global variables in this file guarantee that all
|
||||
* scans in the local address space stay correctly positioned. This
|
||||
* is all we need to worry about, since write locking guarantees that
|
||||
* no one else will be on the same page at the same time as we are.
|
||||
*
|
||||
* The scheme is to manage a list of active scans in the current backend.
|
||||
* Whenever we add or remove records from an index, or whenever we
|
||||
* split a leaf page, we check the list of active scans to see if any
|
||||
* has been affected. A scan is affected only if it is on the same
|
||||
* relation, and the same page, as the update.
|
||||
* The scheme is to manage a list of active scans in the current backend.
|
||||
* Whenever we add or remove records from an index, or whenever we
|
||||
* split a leaf page, we check the list of active scans to see if any
|
||||
* has been affected. A scan is affected only if it is on the same
|
||||
* relation, and the same page, as the update.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -32,83 +32,87 @@
|
||||
#include <storage/bufpage.h>
|
||||
#include <access/nbtree.h>
|
||||
|
||||
typedef struct BTScanListData {
|
||||
IndexScanDesc btsl_scan;
|
||||
struct BTScanListData *btsl_next;
|
||||
} BTScanListData;
|
||||
typedef struct BTScanListData
|
||||
{
|
||||
IndexScanDesc btsl_scan;
|
||||
struct BTScanListData *btsl_next;
|
||||
} BTScanListData;
|
||||
|
||||
typedef BTScanListData *BTScanList;
|
||||
typedef BTScanListData *BTScanList;
|
||||
|
||||
static BTScanList BTScans = (BTScanList) NULL;
|
||||
static BTScanList BTScans = (BTScanList) NULL;
|
||||
|
||||
static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno);
|
||||
static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
static void _bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno);
|
||||
static bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
|
||||
/*
|
||||
* _bt_regscan() -- register a new scan.
|
||||
* _bt_regscan() -- register a new scan.
|
||||
*/
|
||||
void
|
||||
_bt_regscan(IndexScanDesc scan)
|
||||
{
|
||||
BTScanList new_el;
|
||||
|
||||
new_el = (BTScanList) palloc(sizeof(BTScanListData));
|
||||
new_el->btsl_scan = scan;
|
||||
new_el->btsl_next = BTScans;
|
||||
BTScans = new_el;
|
||||
BTScanList new_el;
|
||||
|
||||
new_el = (BTScanList) palloc(sizeof(BTScanListData));
|
||||
new_el->btsl_scan = scan;
|
||||
new_el->btsl_next = BTScans;
|
||||
BTScans = new_el;
|
||||
}
|
||||
|
||||
/*
|
||||
* _bt_dropscan() -- drop a scan from the scan list
|
||||
* _bt_dropscan() -- drop a scan from the scan list
|
||||
*/
|
||||
void
|
||||
_bt_dropscan(IndexScanDesc scan)
|
||||
{
|
||||
BTScanList chk, last;
|
||||
|
||||
last = (BTScanList) NULL;
|
||||
for (chk = BTScans;
|
||||
chk != (BTScanList) NULL && chk->btsl_scan != scan;
|
||||
chk = chk->btsl_next) {
|
||||
last = chk;
|
||||
}
|
||||
|
||||
if (chk == (BTScanList) NULL)
|
||||
elog(WARN, "btree scan list trashed; can't find 0x%lx", scan);
|
||||
|
||||
if (last == (BTScanList) NULL)
|
||||
BTScans = chk->btsl_next;
|
||||
else
|
||||
last->btsl_next = chk->btsl_next;
|
||||
|
||||
pfree (chk);
|
||||
BTScanList chk,
|
||||
last;
|
||||
|
||||
last = (BTScanList) NULL;
|
||||
for (chk = BTScans;
|
||||
chk != (BTScanList) NULL && chk->btsl_scan != scan;
|
||||
chk = chk->btsl_next)
|
||||
{
|
||||
last = chk;
|
||||
}
|
||||
|
||||
if (chk == (BTScanList) NULL)
|
||||
elog(WARN, "btree scan list trashed; can't find 0x%lx", scan);
|
||||
|
||||
if (last == (BTScanList) NULL)
|
||||
BTScans = chk->btsl_next;
|
||||
else
|
||||
last->btsl_next = chk->btsl_next;
|
||||
|
||||
pfree(chk);
|
||||
}
|
||||
|
||||
/*
|
||||
* _bt_adjscans() -- adjust all scans in the scan list to compensate
|
||||
* for a given deletion or insertion
|
||||
* _bt_adjscans() -- adjust all scans in the scan list to compensate
|
||||
* for a given deletion or insertion
|
||||
*/
|
||||
void
|
||||
_bt_adjscans(Relation rel, ItemPointer tid, int op)
|
||||
{
|
||||
BTScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = rel->rd_id;
|
||||
for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) {
|
||||
if (relid == l->btsl_scan->relation->rd_id)
|
||||
_bt_scandel(l->btsl_scan, op,
|
||||
ItemPointerGetBlockNumber(tid),
|
||||
ItemPointerGetOffsetNumber(tid));
|
||||
}
|
||||
BTScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = rel->rd_id;
|
||||
for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next)
|
||||
{
|
||||
if (relid == l->btsl_scan->relation->rd_id)
|
||||
_bt_scandel(l->btsl_scan, op,
|
||||
ItemPointerGetBlockNumber(tid),
|
||||
ItemPointerGetOffsetNumber(tid));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _bt_scandel() -- adjust a single scan
|
||||
* _bt_scandel() -- adjust a single scan
|
||||
*
|
||||
* because each index page is always maintained as an ordered array of
|
||||
* index tuples, the index tuples on a given page shift beneath any
|
||||
* given scan. an index modification "behind" a scan position (i.e.,
|
||||
* given scan. an index modification "behind" a scan position (i.e.,
|
||||
* same page, lower or equal offset number) will therefore force us to
|
||||
* adjust the scan in the following ways:
|
||||
*
|
||||
@@ -126,80 +130,85 @@ _bt_adjscans(Relation rel, ItemPointer tid, int op)
|
||||
static void
|
||||
_bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno)
|
||||
{
|
||||
ItemPointer current;
|
||||
Buffer buf;
|
||||
BTScanOpaque so;
|
||||
|
||||
if (!_bt_scantouched(scan, blkno, offno))
|
||||
return;
|
||||
|
||||
so = (BTScanOpaque) scan->opaque;
|
||||
buf = so->btso_curbuf;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno) {
|
||||
switch (op) {
|
||||
case BT_INSERT:
|
||||
_bt_step(scan, &buf, ForwardScanDirection);
|
||||
break;
|
||||
case BT_DELETE:
|
||||
_bt_step(scan, &buf, BackwardScanDirection);
|
||||
break;
|
||||
default:
|
||||
elog(WARN, "_bt_scandel: bad operation '%d'", op);
|
||||
/*NOTREACHED*/
|
||||
ItemPointer current;
|
||||
Buffer buf;
|
||||
BTScanOpaque so;
|
||||
|
||||
if (!_bt_scantouched(scan, blkno, offno))
|
||||
return;
|
||||
|
||||
so = (BTScanOpaque) scan->opaque;
|
||||
buf = so->btso_curbuf;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case BT_INSERT:
|
||||
_bt_step(scan, &buf, ForwardScanDirection);
|
||||
break;
|
||||
case BT_DELETE:
|
||||
_bt_step(scan, &buf, BackwardScanDirection);
|
||||
break;
|
||||
default:
|
||||
elog(WARN, "_bt_scandel: bad operation '%d'", op);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
so->btso_curbuf = buf;
|
||||
}
|
||||
so->btso_curbuf = buf;
|
||||
}
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno) {
|
||||
ItemPointerData tmp;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
switch (op) {
|
||||
case BT_INSERT:
|
||||
_bt_step(scan, &buf, ForwardScanDirection);
|
||||
break;
|
||||
case BT_DELETE:
|
||||
_bt_step(scan, &buf, BackwardScanDirection);
|
||||
break;
|
||||
default:
|
||||
elog(WARN, "_bt_scandel: bad operation '%d'", op);
|
||||
/*NOTREACHED*/
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
{
|
||||
ItemPointerData tmp;
|
||||
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
switch (op)
|
||||
{
|
||||
case BT_INSERT:
|
||||
_bt_step(scan, &buf, ForwardScanDirection);
|
||||
break;
|
||||
case BT_DELETE:
|
||||
_bt_step(scan, &buf, BackwardScanDirection);
|
||||
break;
|
||||
default:
|
||||
elog(WARN, "_bt_scandel: bad operation '%d'", op);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
so->btso_mrkbuf = buf;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
}
|
||||
so->btso_mrkbuf = buf;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _bt_scantouched() -- check to see if a scan is affected by a given
|
||||
* change to the index
|
||||
* _bt_scantouched() -- check to see if a scan is affected by a given
|
||||
* change to the index
|
||||
*/
|
||||
static bool
|
||||
static bool
|
||||
_bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
|
||||
{
|
||||
ItemPointer current;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
ItemPointer current;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* btstrat.c--
|
||||
* Srategy map entries for the btree indexed access method
|
||||
* Srategy map entries for the btree indexed access method
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.4 1996/11/05 10:35:37 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.5 1997/09/07 04:39:04 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -20,111 +20,111 @@
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* StrategyNegate, StrategyCommute, and StrategyNegateCommute
|
||||
* assume <, <=, ==, >=, > ordering.
|
||||
* StrategyNegate, StrategyCommute, and StrategyNegateCommute
|
||||
* assume <, <=, ==, >=, > ordering.
|
||||
*/
|
||||
static StrategyNumber BTNegate[5] = {
|
||||
BTGreaterEqualStrategyNumber,
|
||||
BTGreaterStrategyNumber,
|
||||
InvalidStrategy,
|
||||
BTLessStrategyNumber,
|
||||
BTLessEqualStrategyNumber
|
||||
static StrategyNumber BTNegate[5] = {
|
||||
BTGreaterEqualStrategyNumber,
|
||||
BTGreaterStrategyNumber,
|
||||
InvalidStrategy,
|
||||
BTLessStrategyNumber,
|
||||
BTLessEqualStrategyNumber
|
||||
};
|
||||
|
||||
static StrategyNumber BTCommute[5] = {
|
||||
BTGreaterStrategyNumber,
|
||||
BTGreaterEqualStrategyNumber,
|
||||
InvalidStrategy,
|
||||
BTLessEqualStrategyNumber,
|
||||
BTLessStrategyNumber
|
||||
static StrategyNumber BTCommute[5] = {
|
||||
BTGreaterStrategyNumber,
|
||||
BTGreaterEqualStrategyNumber,
|
||||
InvalidStrategy,
|
||||
BTLessEqualStrategyNumber,
|
||||
BTLessStrategyNumber
|
||||
};
|
||||
|
||||
static StrategyNumber BTNegateCommute[5] = {
|
||||
BTLessEqualStrategyNumber,
|
||||
BTLessStrategyNumber,
|
||||
InvalidStrategy,
|
||||
BTGreaterStrategyNumber,
|
||||
BTGreaterEqualStrategyNumber
|
||||
static StrategyNumber BTNegateCommute[5] = {
|
||||
BTLessEqualStrategyNumber,
|
||||
BTLessStrategyNumber,
|
||||
InvalidStrategy,
|
||||
BTGreaterStrategyNumber,
|
||||
BTGreaterEqualStrategyNumber
|
||||
};
|
||||
|
||||
static uint16 BTLessTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTLessStrategyNumber,
|
||||
SK_NEGATE,
|
||||
BTLessStrategyNumber,
|
||||
SK_NEGATE | SK_COMMUTE
|
||||
static uint16 BTLessTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTLessStrategyNumber,
|
||||
SK_NEGATE,
|
||||
BTLessStrategyNumber,
|
||||
SK_NEGATE | SK_COMMUTE
|
||||
};
|
||||
|
||||
static uint16 BTLessEqualTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTLessEqualStrategyNumber,
|
||||
0x0,
|
||||
BTLessEqualStrategyNumber,
|
||||
SK_COMMUTE
|
||||
static uint16 BTLessEqualTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTLessEqualStrategyNumber,
|
||||
0x0,
|
||||
BTLessEqualStrategyNumber,
|
||||
SK_COMMUTE
|
||||
};
|
||||
|
||||
static uint16 BTGreaterEqualTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTGreaterEqualStrategyNumber,
|
||||
0x0,
|
||||
BTGreaterEqualStrategyNumber,
|
||||
SK_COMMUTE
|
||||
};
|
||||
|
||||
static uint16 BTGreaterTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTGreaterStrategyNumber,
|
||||
SK_NEGATE,
|
||||
BTGreaterStrategyNumber,
|
||||
SK_NEGATE | SK_COMMUTE
|
||||
2,
|
||||
BTGreaterEqualStrategyNumber,
|
||||
0x0,
|
||||
BTGreaterEqualStrategyNumber,
|
||||
SK_COMMUTE
|
||||
};
|
||||
|
||||
static StrategyTerm BTEqualExpressionData[] = {
|
||||
(StrategyTerm)BTLessTermData, /* XXX */
|
||||
(StrategyTerm)BTLessEqualTermData, /* XXX */
|
||||
(StrategyTerm)BTGreaterEqualTermData, /* XXX */
|
||||
(StrategyTerm)BTGreaterTermData, /* XXX */
|
||||
NULL
|
||||
static uint16 BTGreaterTermData[] = { /* XXX type clash */
|
||||
2,
|
||||
BTGreaterStrategyNumber,
|
||||
SK_NEGATE,
|
||||
BTGreaterStrategyNumber,
|
||||
SK_NEGATE | SK_COMMUTE
|
||||
};
|
||||
|
||||
static StrategyEvaluationData BTEvaluationData = {
|
||||
/* XXX static for simplicity */
|
||||
|
||||
BTMaxStrategyNumber,
|
||||
(StrategyTransformMap)BTNegate, /* XXX */
|
||||
(StrategyTransformMap)BTCommute, /* XXX */
|
||||
(StrategyTransformMap)BTNegateCommute, /* XXX */
|
||||
static StrategyTerm BTEqualExpressionData[] = {
|
||||
(StrategyTerm) BTLessTermData, /* XXX */
|
||||
(StrategyTerm) BTLessEqualTermData, /* XXX */
|
||||
(StrategyTerm) BTGreaterEqualTermData, /* XXX */
|
||||
(StrategyTerm) BTGreaterTermData, /* XXX */
|
||||
NULL
|
||||
};
|
||||
|
||||
{ NULL, NULL, (StrategyExpression)BTEqualExpressionData, NULL, NULL,
|
||||
NULL,NULL,NULL,NULL,NULL,NULL,NULL}
|
||||
static StrategyEvaluationData BTEvaluationData = {
|
||||
/* XXX static for simplicity */
|
||||
|
||||
BTMaxStrategyNumber,
|
||||
(StrategyTransformMap) BTNegate, /* XXX */
|
||||
(StrategyTransformMap) BTCommute, /* XXX */
|
||||
(StrategyTransformMap) BTNegateCommute, /* XXX */
|
||||
|
||||
{NULL, NULL, (StrategyExpression) BTEqualExpressionData, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* RelationGetBTStrategy
|
||||
* RelationGetBTStrategy
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
StrategyNumber
|
||||
_bt_getstrat(Relation rel,
|
||||
AttrNumber attno,
|
||||
RegProcedure proc)
|
||||
AttrNumber attno,
|
||||
RegProcedure proc)
|
||||
{
|
||||
StrategyNumber strat;
|
||||
|
||||
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
|
||||
|
||||
Assert(StrategyNumberIsValid(strat));
|
||||
|
||||
return (strat);
|
||||
StrategyNumber strat;
|
||||
|
||||
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
|
||||
|
||||
Assert(StrategyNumberIsValid(strat));
|
||||
|
||||
return (strat);
|
||||
}
|
||||
|
||||
bool
|
||||
_bt_invokestrat(Relation rel,
|
||||
AttrNumber attno,
|
||||
StrategyNumber strat,
|
||||
Datum left,
|
||||
Datum right)
|
||||
AttrNumber attno,
|
||||
StrategyNumber strat,
|
||||
Datum left,
|
||||
Datum right)
|
||||
{
|
||||
return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat,
|
||||
left, right));
|
||||
return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat,
|
||||
left, right));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* btutils.c--
|
||||
* Utility code for Postgres btree implementation.
|
||||
* Utility code for Postgres btree implementation.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.11 1997/08/19 21:29:47 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.12 1997/09/07 04:39:05 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -23,367 +23,384 @@
|
||||
#include <catalog/pg_proc.h>
|
||||
#include <executor/execdebug.h>
|
||||
|
||||
extern int NIndexTupleProcessed;
|
||||
extern int NIndexTupleProcessed;
|
||||
|
||||
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
ScanKey
|
||||
ScanKey
|
||||
_bt_mkscankey(Relation rel, IndexTuple itup)
|
||||
{
|
||||
ScanKey skey;
|
||||
TupleDesc itupdesc;
|
||||
int natts;
|
||||
int i;
|
||||
Datum arg;
|
||||
RegProcedure proc;
|
||||
bool null;
|
||||
bits16 flag;
|
||||
|
||||
natts = rel->rd_rel->relnatts;
|
||||
itupdesc = RelationGetTupleDescriptor(rel);
|
||||
|
||||
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
|
||||
|
||||
for (i = 0; i < natts; i++) {
|
||||
arg = index_getattr(itup, i + 1, itupdesc, &null);
|
||||
if ( null )
|
||||
{
|
||||
ScanKey skey;
|
||||
TupleDesc itupdesc;
|
||||
int natts;
|
||||
int i;
|
||||
Datum arg;
|
||||
RegProcedure proc;
|
||||
bool null;
|
||||
bits16 flag;
|
||||
|
||||
natts = rel->rd_rel->relnatts;
|
||||
itupdesc = RelationGetTupleDescriptor(rel);
|
||||
|
||||
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
|
||||
|
||||
for (i = 0; i < natts; i++)
|
||||
{
|
||||
proc = NullValueRegProcedure;
|
||||
flag = SK_ISNULL;
|
||||
arg = index_getattr(itup, i + 1, itupdesc, &null);
|
||||
if (null)
|
||||
{
|
||||
proc = NullValueRegProcedure;
|
||||
flag = SK_ISNULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
proc = index_getprocid(rel, i + 1, BTORDER_PROC);
|
||||
flag = 0x0;
|
||||
}
|
||||
ScanKeyEntryInitialize(&skey[i],
|
||||
flag, (AttrNumber) (i + 1), proc, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
proc = index_getprocid(rel, i + 1, BTORDER_PROC);
|
||||
flag = 0x0;
|
||||
}
|
||||
ScanKeyEntryInitialize(&skey[i],
|
||||
flag, (AttrNumber) (i + 1), proc, arg);
|
||||
}
|
||||
|
||||
return (skey);
|
||||
|
||||
return (skey);
|
||||
}
|
||||
|
||||
void
|
||||
_bt_freeskey(ScanKey skey)
|
||||
{
|
||||
pfree(skey);
|
||||
pfree(skey);
|
||||
}
|
||||
|
||||
void
|
||||
_bt_freestack(BTStack stack)
|
||||
{
|
||||
BTStack ostack;
|
||||
|
||||
while (stack != (BTStack) NULL) {
|
||||
ostack = stack;
|
||||
stack = stack->bts_parent;
|
||||
pfree(ostack->bts_btitem);
|
||||
pfree(ostack);
|
||||
}
|
||||
BTStack ostack;
|
||||
|
||||
while (stack != (BTStack) NULL)
|
||||
{
|
||||
ostack = stack;
|
||||
stack = stack->bts_parent;
|
||||
pfree(ostack->bts_btitem);
|
||||
pfree(ostack);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals.
|
||||
* _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals.
|
||||
*
|
||||
* The order of the keys in the qual match the ordering imposed by
|
||||
* the index. This routine only needs to be called if there are
|
||||
* more than one qual clauses using this index.
|
||||
* The order of the keys in the qual match the ordering imposed by
|
||||
* the index. This routine only needs to be called if there are
|
||||
* more than one qual clauses using this index.
|
||||
*/
|
||||
void
|
||||
_bt_orderkeys(Relation relation, BTScanOpaque so)
|
||||
{
|
||||
ScanKey xform;
|
||||
ScanKeyData *cur;
|
||||
StrategyMap map;
|
||||
int nbytes;
|
||||
long test;
|
||||
int i, j;
|
||||
int init[BTMaxStrategyNumber+1];
|
||||
ScanKey key;
|
||||
uint16 numberOfKeys = so->numberOfKeys;
|
||||
uint16 new_numberOfKeys = 0;
|
||||
AttrNumber attno = 1;
|
||||
|
||||
if ( numberOfKeys < 1 )
|
||||
return;
|
||||
|
||||
key = so->keyData;
|
||||
|
||||
cur = &key[0];
|
||||
if ( cur->sk_attno != 1 )
|
||||
elog (WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
|
||||
|
||||
if ( numberOfKeys == 1 )
|
||||
{
|
||||
/*
|
||||
* We don't use indices for 'A is null' and 'A is not null'
|
||||
* currently and 'A < = > <> NULL' is non-sense' - so
|
||||
* qual is not Ok. - vadim 03/21/97
|
||||
*/
|
||||
if ( cur->sk_flags & SK_ISNULL )
|
||||
so->qual_ok = 0;
|
||||
so->numberOfFirstKeys = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* get space for the modified array of keys */
|
||||
nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
|
||||
xform = (ScanKey) palloc(nbytes);
|
||||
ScanKey xform;
|
||||
ScanKeyData *cur;
|
||||
StrategyMap map;
|
||||
int nbytes;
|
||||
long test;
|
||||
int i,
|
||||
j;
|
||||
int init[BTMaxStrategyNumber + 1];
|
||||
ScanKey key;
|
||||
uint16 numberOfKeys = so->numberOfKeys;
|
||||
uint16 new_numberOfKeys = 0;
|
||||
AttrNumber attno = 1;
|
||||
|
||||
memset(xform, 0, nbytes);
|
||||
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
|
||||
BTMaxStrategyNumber,
|
||||
attno);
|
||||
for (j = 0; j <= BTMaxStrategyNumber; j++)
|
||||
init[j] = 0;
|
||||
|
||||
/* check each key passed in */
|
||||
for (i = 0; ; )
|
||||
{
|
||||
if ( i < numberOfKeys )
|
||||
cur = &key[i];
|
||||
if (numberOfKeys < 1)
|
||||
return;
|
||||
|
||||
if ( cur->sk_flags & SK_ISNULL ) /* see comments above */
|
||||
so->qual_ok = 0;
|
||||
key = so->keyData;
|
||||
|
||||
if ( i == numberOfKeys || cur->sk_attno != attno )
|
||||
cur = &key[0];
|
||||
if (cur->sk_attno != 1)
|
||||
elog(WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
|
||||
|
||||
if (numberOfKeys == 1)
|
||||
{
|
||||
if ( cur->sk_attno != attno + 1 && i < numberOfKeys )
|
||||
{
|
||||
elog (WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1);
|
||||
}
|
||||
/*
|
||||
* If = has been specified, no other key will be used.
|
||||
* In case of key < 2 && key == 1 and so on
|
||||
* we have to set qual_ok to 0
|
||||
*/
|
||||
if (init[BTEqualStrategyNumber - 1])
|
||||
{
|
||||
ScanKeyData *eq, *chk;
|
||||
|
||||
eq = &xform[BTEqualStrategyNumber - 1];
|
||||
for (j = BTMaxStrategyNumber; --j >= 0; )
|
||||
{
|
||||
if ( j == (BTEqualStrategyNumber - 1) || init[j] == 0 )
|
||||
continue;
|
||||
chk = &xform[j];
|
||||
test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument);
|
||||
if (!test)
|
||||
so->qual_ok = 0;
|
||||
}
|
||||
init[BTLessStrategyNumber - 1] = 0;
|
||||
init[BTLessEqualStrategyNumber - 1] = 0;
|
||||
init[BTGreaterEqualStrategyNumber - 1] = 0;
|
||||
init[BTGreaterStrategyNumber - 1] = 0;
|
||||
}
|
||||
|
||||
/* only one of <, <= */
|
||||
if (init[BTLessStrategyNumber - 1]
|
||||
&& init[BTLessEqualStrategyNumber - 1])
|
||||
{
|
||||
ScanKeyData *lt, *le;
|
||||
|
||||
lt = &xform[BTLessStrategyNumber - 1];
|
||||
le = &xform[BTLessEqualStrategyNumber - 1];
|
||||
/*
|
||||
* DO NOT use the cached function stuff here -- this is key
|
||||
* ordering, happens only when the user expresses a hokey
|
||||
* qualification, and gets executed only once, anyway. The
|
||||
* transform maps are hard-coded, and can't be initialized
|
||||
* in the correct way.
|
||||
* We don't use indices for 'A is null' and 'A is not null'
|
||||
* currently and 'A < = > <> NULL' is non-sense' - so qual is not
|
||||
* Ok. - vadim 03/21/97
|
||||
*/
|
||||
test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument);
|
||||
if (test)
|
||||
init[BTLessEqualStrategyNumber - 1] = 0;
|
||||
else
|
||||
init[BTLessStrategyNumber - 1] = 0;
|
||||
}
|
||||
|
||||
/* only one of >, >= */
|
||||
if (init[BTGreaterStrategyNumber - 1]
|
||||
&& init[BTGreaterEqualStrategyNumber - 1])
|
||||
{
|
||||
ScanKeyData *gt, *ge;
|
||||
|
||||
gt = &xform[BTGreaterStrategyNumber - 1];
|
||||
ge = &xform[BTGreaterEqualStrategyNumber - 1];
|
||||
|
||||
/* see note above on function cache */
|
||||
test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument);
|
||||
if (test)
|
||||
init[BTGreaterEqualStrategyNumber - 1] = 0;
|
||||
else
|
||||
init[BTGreaterStrategyNumber - 1] = 0;
|
||||
}
|
||||
|
||||
/* okay, reorder and count */
|
||||
for (j = BTMaxStrategyNumber; --j >= 0; )
|
||||
if (init[j])
|
||||
key[new_numberOfKeys++] = xform[j];
|
||||
|
||||
if ( attno == 1 )
|
||||
so->numberOfFirstKeys = new_numberOfKeys;
|
||||
|
||||
if ( i == numberOfKeys )
|
||||
break;
|
||||
if (cur->sk_flags & SK_ISNULL)
|
||||
so->qual_ok = 0;
|
||||
so->numberOfFirstKeys = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialization for new attno */
|
||||
attno = cur->sk_attno;
|
||||
memset(xform, 0, nbytes);
|
||||
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
|
||||
BTMaxStrategyNumber,
|
||||
attno);
|
||||
/* haven't looked at any strategies yet */
|
||||
for (j = 0; j <= BTMaxStrategyNumber; j++)
|
||||
/* get space for the modified array of keys */
|
||||
nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
|
||||
xform = (ScanKey) palloc(nbytes);
|
||||
|
||||
memset(xform, 0, nbytes);
|
||||
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
|
||||
BTMaxStrategyNumber,
|
||||
attno);
|
||||
for (j = 0; j <= BTMaxStrategyNumber; j++)
|
||||
init[j] = 0;
|
||||
|
||||
/* check each key passed in */
|
||||
for (i = 0;;)
|
||||
{
|
||||
if (i < numberOfKeys)
|
||||
cur = &key[i];
|
||||
|
||||
if (cur->sk_flags & SK_ISNULL) /* see comments above */
|
||||
so->qual_ok = 0;
|
||||
|
||||
if (i == numberOfKeys || cur->sk_attno != attno)
|
||||
{
|
||||
if (cur->sk_attno != attno + 1 && i < numberOfKeys)
|
||||
{
|
||||
elog(WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If = has been specified, no other key will be used. In case
|
||||
* of key < 2 && key == 1 and so on we have to set qual_ok to
|
||||
* 0
|
||||
*/
|
||||
if (init[BTEqualStrategyNumber - 1])
|
||||
{
|
||||
ScanKeyData *eq,
|
||||
*chk;
|
||||
|
||||
eq = &xform[BTEqualStrategyNumber - 1];
|
||||
for (j = BTMaxStrategyNumber; --j >= 0;)
|
||||
{
|
||||
if (j == (BTEqualStrategyNumber - 1) || init[j] == 0)
|
||||
continue;
|
||||
chk = &xform[j];
|
||||
test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument);
|
||||
if (!test)
|
||||
so->qual_ok = 0;
|
||||
}
|
||||
init[BTLessStrategyNumber - 1] = 0;
|
||||
init[BTLessEqualStrategyNumber - 1] = 0;
|
||||
init[BTGreaterEqualStrategyNumber - 1] = 0;
|
||||
init[BTGreaterStrategyNumber - 1] = 0;
|
||||
}
|
||||
|
||||
/* only one of <, <= */
|
||||
if (init[BTLessStrategyNumber - 1]
|
||||
&& init[BTLessEqualStrategyNumber - 1])
|
||||
{
|
||||
ScanKeyData *lt,
|
||||
*le;
|
||||
|
||||
lt = &xform[BTLessStrategyNumber - 1];
|
||||
le = &xform[BTLessEqualStrategyNumber - 1];
|
||||
|
||||
/*
|
||||
* DO NOT use the cached function stuff here -- this is
|
||||
* key ordering, happens only when the user expresses a
|
||||
* hokey qualification, and gets executed only once,
|
||||
* anyway. The transform maps are hard-coded, and can't
|
||||
* be initialized in the correct way.
|
||||
*/
|
||||
test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument);
|
||||
if (test)
|
||||
init[BTLessEqualStrategyNumber - 1] = 0;
|
||||
else
|
||||
init[BTLessStrategyNumber - 1] = 0;
|
||||
}
|
||||
|
||||
/* only one of >, >= */
|
||||
if (init[BTGreaterStrategyNumber - 1]
|
||||
&& init[BTGreaterEqualStrategyNumber - 1])
|
||||
{
|
||||
ScanKeyData *gt,
|
||||
*ge;
|
||||
|
||||
gt = &xform[BTGreaterStrategyNumber - 1];
|
||||
ge = &xform[BTGreaterEqualStrategyNumber - 1];
|
||||
|
||||
/* see note above on function cache */
|
||||
test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument);
|
||||
if (test)
|
||||
init[BTGreaterEqualStrategyNumber - 1] = 0;
|
||||
else
|
||||
init[BTGreaterStrategyNumber - 1] = 0;
|
||||
}
|
||||
|
||||
/* okay, reorder and count */
|
||||
for (j = BTMaxStrategyNumber; --j >= 0;)
|
||||
if (init[j])
|
||||
key[new_numberOfKeys++] = xform[j];
|
||||
|
||||
if (attno == 1)
|
||||
so->numberOfFirstKeys = new_numberOfKeys;
|
||||
|
||||
if (i == numberOfKeys)
|
||||
break;
|
||||
|
||||
/* initialization for new attno */
|
||||
attno = cur->sk_attno;
|
||||
memset(xform, 0, nbytes);
|
||||
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
|
||||
BTMaxStrategyNumber,
|
||||
attno);
|
||||
/* haven't looked at any strategies yet */
|
||||
for (j = 0; j <= BTMaxStrategyNumber; j++)
|
||||
init[j] = 0;
|
||||
}
|
||||
|
||||
for (j = BTMaxStrategyNumber; --j >= 0;)
|
||||
{
|
||||
if (cur->sk_procedure == map->entry[j].sk_procedure)
|
||||
break;
|
||||
}
|
||||
|
||||
/* have we seen one of these before? */
|
||||
if (init[j])
|
||||
{
|
||||
/* yup, use the appropriate value */
|
||||
test =
|
||||
(long) FMGR_PTR2(cur->sk_func, cur->sk_procedure,
|
||||
cur->sk_argument, xform[j].sk_argument);
|
||||
if (test)
|
||||
xform[j].sk_argument = cur->sk_argument;
|
||||
else if (j == (BTEqualStrategyNumber - 1))
|
||||
so->qual_ok = 0;/* key == a && key == b, but a != b */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* nope, use this value */
|
||||
memmove(&xform[j], cur, sizeof(*cur));
|
||||
init[j] = 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
for (j = BTMaxStrategyNumber; --j >= 0; )
|
||||
{
|
||||
if (cur->sk_procedure == map->entry[j].sk_procedure)
|
||||
break;
|
||||
}
|
||||
|
||||
/* have we seen one of these before? */
|
||||
if (init[j])
|
||||
{
|
||||
/* yup, use the appropriate value */
|
||||
test =
|
||||
(long) FMGR_PTR2(cur->sk_func, cur->sk_procedure,
|
||||
cur->sk_argument, xform[j].sk_argument);
|
||||
if (test)
|
||||
xform[j].sk_argument = cur->sk_argument;
|
||||
else if ( j == (BTEqualStrategyNumber - 1) )
|
||||
so->qual_ok = 0; /* key == a && key == b, but a != b */
|
||||
} else
|
||||
{
|
||||
/* nope, use this value */
|
||||
memmove(&xform[j], cur, sizeof(*cur));
|
||||
init[j] = 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
so->numberOfKeys = new_numberOfKeys;
|
||||
|
||||
pfree(xform);
|
||||
so->numberOfKeys = new_numberOfKeys;
|
||||
|
||||
pfree(xform);
|
||||
}
|
||||
|
||||
BTItem
|
||||
_bt_formitem(IndexTuple itup)
|
||||
{
|
||||
int nbytes_btitem;
|
||||
BTItem btitem;
|
||||
Size tuplen;
|
||||
extern Oid newoid();
|
||||
|
||||
/* see comments in btbuild
|
||||
|
||||
if (itup->t_info & INDEX_NULL_MASK)
|
||||
elog(WARN, "btree indices cannot include null keys");
|
||||
*/
|
||||
|
||||
/* make a copy of the index tuple with room for the sequence number */
|
||||
tuplen = IndexTupleSize(itup);
|
||||
nbytes_btitem = tuplen +
|
||||
(sizeof(BTItemData) - sizeof(IndexTupleData));
|
||||
|
||||
btitem = (BTItem) palloc(nbytes_btitem);
|
||||
memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
|
||||
|
||||
int nbytes_btitem;
|
||||
BTItem btitem;
|
||||
Size tuplen;
|
||||
extern Oid newoid();
|
||||
|
||||
/*
|
||||
* see comments in btbuild
|
||||
*
|
||||
* if (itup->t_info & INDEX_NULL_MASK) elog(WARN, "btree indices cannot
|
||||
* include null keys");
|
||||
*/
|
||||
|
||||
/* make a copy of the index tuple with room for the sequence number */
|
||||
tuplen = IndexTupleSize(itup);
|
||||
nbytes_btitem = tuplen +
|
||||
(sizeof(BTItemData) - sizeof(IndexTupleData));
|
||||
|
||||
btitem = (BTItem) palloc(nbytes_btitem);
|
||||
memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
|
||||
|
||||
#ifndef BTREE_VERSION_1
|
||||
btitem->bti_oid = newoid();
|
||||
btitem->bti_oid = newoid();
|
||||
#endif
|
||||
return (btitem);
|
||||
return (btitem);
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
bool
|
||||
_bt_checkqual(IndexScanDesc scan, IndexTuple itup)
|
||||
{
|
||||
BTScanOpaque so;
|
||||
|
||||
so = (BTScanOpaque) scan->opaque;
|
||||
if (so->numberOfKeys > 0)
|
||||
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
|
||||
so->numberOfKeys, so->keyData));
|
||||
else
|
||||
return (true);
|
||||
BTScanOpaque so;
|
||||
|
||||
so = (BTScanOpaque) scan->opaque;
|
||||
if (so->numberOfKeys > 0)
|
||||
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
|
||||
so->numberOfKeys, so->keyData));
|
||||
else
|
||||
return (true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USED
|
||||
bool
|
||||
_bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz)
|
||||
{
|
||||
BTScanOpaque so;
|
||||
|
||||
so = (BTScanOpaque) scan->opaque;
|
||||
if ( keysz > 0 && so->numberOfKeys >= keysz )
|
||||
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
|
||||
keysz, so->keyData));
|
||||
else
|
||||
return (true);
|
||||
BTScanOpaque so;
|
||||
|
||||
so = (BTScanOpaque) scan->opaque;
|
||||
if (keysz > 0 && so->numberOfKeys >= keysz)
|
||||
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
|
||||
keysz, so->keyData));
|
||||
else
|
||||
return (true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool
|
||||
_bt_checkkeys (IndexScanDesc scan, IndexTuple tuple, Size *keysok)
|
||||
_bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size * keysok)
|
||||
{
|
||||
BTScanOpaque so = (BTScanOpaque) scan->opaque;
|
||||
Size keysz = so->numberOfKeys;
|
||||
TupleDesc tupdesc;
|
||||
ScanKey key;
|
||||
Datum datum;
|
||||
bool isNull;
|
||||
int test;
|
||||
|
||||
*keysok = 0;
|
||||
if ( keysz == 0 )
|
||||
return (true);
|
||||
|
||||
key = so->keyData;
|
||||
tupdesc = RelationGetTupleDescriptor(scan->relation);
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
while (keysz > 0)
|
||||
{
|
||||
datum = index_getattr(tuple,
|
||||
key[0].sk_attno,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
|
||||
/* btree doesn't support 'A is null' clauses, yet */
|
||||
if ( isNull || key[0].sk_flags & SK_ISNULL )
|
||||
BTScanOpaque so = (BTScanOpaque) scan->opaque;
|
||||
Size keysz = so->numberOfKeys;
|
||||
TupleDesc tupdesc;
|
||||
ScanKey key;
|
||||
Datum datum;
|
||||
bool isNull;
|
||||
int test;
|
||||
|
||||
*keysok = 0;
|
||||
if (keysz == 0)
|
||||
return (true);
|
||||
|
||||
key = so->keyData;
|
||||
tupdesc = RelationGetTupleDescriptor(scan->relation);
|
||||
|
||||
IncrIndexProcessed();
|
||||
|
||||
while (keysz > 0)
|
||||
{
|
||||
return (false);
|
||||
datum = index_getattr(tuple,
|
||||
key[0].sk_attno,
|
||||
tupdesc,
|
||||
&isNull);
|
||||
|
||||
/* btree doesn't support 'A is null' clauses, yet */
|
||||
if (isNull || key[0].sk_flags & SK_ISNULL)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE)
|
||||
{
|
||||
test = (int) (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
datum);
|
||||
}
|
||||
else
|
||||
{
|
||||
test = (int) (*(key[0].sk_func))
|
||||
(datum,
|
||||
DatumGetPointer(key[0].sk_argument));
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
keysz -= 1;
|
||||
key++;
|
||||
(*keysok)++;
|
||||
}
|
||||
|
||||
if (key[0].sk_flags & SK_COMMUTE) {
|
||||
test = (int) (*(key[0].sk_func))
|
||||
(DatumGetPointer(key[0].sk_argument),
|
||||
datum);
|
||||
} else {
|
||||
test = (int) (*(key[0].sk_func))
|
||||
(datum,
|
||||
DatumGetPointer(key[0].sk_argument));
|
||||
}
|
||||
|
||||
if (!test == !(key[0].sk_flags & SK_NEGATE)) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
keysz -= 1;
|
||||
key++;
|
||||
(*keysok)++;
|
||||
}
|
||||
|
||||
return (true);
|
||||
return (true);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* rtget.c--
|
||||
* fetch tuples from an rtree scan.
|
||||
* fetch tuples from an rtree scan.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.7 1996/11/21 06:13:43 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.8 1997/09/07 04:39:11 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <storage/bufmgr.h>
|
||||
#include <access/sdir.h>
|
||||
#include <access/relscan.h>
|
||||
@@ -21,14 +21,15 @@
|
||||
#include <access/rtree.h>
|
||||
#include <storage/bufpage.h>
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
|
||||
static OffsetNumber findnext(IndexScanDesc s, Page p, OffsetNumber n,
|
||||
ScanDirection dir);
|
||||
static OffsetNumber
|
||||
findnext(IndexScanDesc s, Page p, OffsetNumber n,
|
||||
ScanDirection dir);
|
||||
static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir);
|
||||
static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir);
|
||||
static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir);
|
||||
@@ -38,278 +39,315 @@ static ItemPointer rtheapptr(Relation r, ItemPointer itemp);
|
||||
RetrieveIndexResult
|
||||
rtgettuple(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
RetrieveIndexResult res;
|
||||
|
||||
/* if we have it cached in the scan desc, just return the value */
|
||||
if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL)
|
||||
RetrieveIndexResult res;
|
||||
|
||||
/* if we have it cached in the scan desc, just return the value */
|
||||
if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL)
|
||||
return (res);
|
||||
|
||||
/* not cached, so we'll have to do some work */
|
||||
if (ItemPointerIsValid(&(s->currentItemData)))
|
||||
{
|
||||
res = rtnext(s, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = rtfirst(s, dir);
|
||||
}
|
||||
return (res);
|
||||
|
||||
/* not cached, so we'll have to do some work */
|
||||
if (ItemPointerIsValid(&(s->currentItemData))) {
|
||||
res = rtnext(s, dir);
|
||||
} else {
|
||||
res = rtfirst(s, dir);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
static RetrieveIndexResult
|
||||
static RetrieveIndexResult
|
||||
rtfirst(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
RTreePageOpaque po;
|
||||
RTreeScanOpaque so;
|
||||
RTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
b = ReadBuffer(s->relation, P_ROOT);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
for (;;) {
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
n = findnext(s, p, maxoff, dir);
|
||||
else
|
||||
n = findnext(s, p, FirstOffsetNumber, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff) {
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (RTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->rts_blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = OffsetNumberPrev(stk->rts_child);
|
||||
} else {
|
||||
n = OffsetNumberNext(stk->rts_child);
|
||||
}
|
||||
so->s_stack = stk->rts_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = findnext(s, p, n, dir);
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
RTreePageOpaque po;
|
||||
RTreeScanOpaque so;
|
||||
RTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
b = ReadBuffer(s->relation, P_ROOT);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
n = findnext(s, p, maxoff, dir);
|
||||
else
|
||||
n = findnext(s, p, FirstOffsetNumber, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff)
|
||||
{
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (RTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->rts_blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = OffsetNumberPrev(stk->rts_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberNext(stk->rts_child);
|
||||
}
|
||||
so->s_stack = stk->rts_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = findnext(s, p, n, dir);
|
||||
}
|
||||
if (po->flags & F_LEAF)
|
||||
{
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
}
|
||||
else
|
||||
{
|
||||
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
stk->rts_child = n;
|
||||
stk->rts_blk = BufferGetBlockNumber(b);
|
||||
stk->rts_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
}
|
||||
}
|
||||
if (po->flags & F_LEAF) {
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
} else {
|
||||
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
stk->rts_child = n;
|
||||
stk->rts_blk = BufferGetBlockNumber(b);
|
||||
stk->rts_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RetrieveIndexResult
|
||||
static RetrieveIndexResult
|
||||
rtnext(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
RTreePageOpaque po;
|
||||
RTreeScanOpaque so;
|
||||
RTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
|
||||
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
|
||||
|
||||
if (ScanDirectionIsForward(dir)) {
|
||||
n = OffsetNumberNext(n);
|
||||
} else {
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
Buffer b;
|
||||
Page p;
|
||||
OffsetNumber n;
|
||||
OffsetNumber maxoff;
|
||||
RetrieveIndexResult res;
|
||||
RTreePageOpaque po;
|
||||
RTreeScanOpaque so;
|
||||
RTSTACK *stk;
|
||||
BlockNumber blk;
|
||||
IndexTuple it;
|
||||
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
for (;;) {
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
n = findnext(s, p, n, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff) {
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (RTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->rts_blk);
|
||||
p = BufferGetPage(b);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = OffsetNumberPrev(stk->rts_child);
|
||||
} else {
|
||||
n = OffsetNumberNext(stk->rts_child);
|
||||
}
|
||||
so->s_stack = stk->rts_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = findnext(s, p, n, dir);
|
||||
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
|
||||
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
|
||||
|
||||
if (ScanDirectionIsForward(dir))
|
||||
{
|
||||
n = OffsetNumberNext(n);
|
||||
}
|
||||
if (po->flags & F_LEAF) {
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
} else {
|
||||
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
stk->rts_child = n;
|
||||
stk->rts_blk = BufferGetBlockNumber(b);
|
||||
stk->rts_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = PageGetMaxOffsetNumber(p);
|
||||
} else {
|
||||
n = FirstOffsetNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
n = findnext(s, p, n, dir);
|
||||
|
||||
while (n < FirstOffsetNumber || n > maxoff)
|
||||
{
|
||||
|
||||
ReleaseBuffer(b);
|
||||
if (so->s_stack == (RTSTACK *) NULL)
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
|
||||
stk = so->s_stack;
|
||||
b = ReadBuffer(s->relation, stk->rts_blk);
|
||||
p = BufferGetPage(b);
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = OffsetNumberPrev(stk->rts_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberNext(stk->rts_child);
|
||||
}
|
||||
so->s_stack = stk->rts_parent;
|
||||
pfree(stk);
|
||||
|
||||
n = findnext(s, p, n, dir);
|
||||
}
|
||||
if (po->flags & F_LEAF)
|
||||
{
|
||||
ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), &(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
return (res);
|
||||
}
|
||||
else
|
||||
{
|
||||
stk = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
stk->rts_child = n;
|
||||
stk->rts_blk = BufferGetBlockNumber(b);
|
||||
stk->rts_parent = so->s_stack;
|
||||
so->s_stack = stk;
|
||||
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
blk = ItemPointerGetBlockNumber(&(it->t_tid));
|
||||
|
||||
ReleaseBuffer(b);
|
||||
b = ReadBuffer(s->relation, blk);
|
||||
p = BufferGetPage(b);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = PageGetMaxOffsetNumber(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = FirstOffsetNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static OffsetNumber
|
||||
static OffsetNumber
|
||||
findnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
|
||||
{
|
||||
OffsetNumber maxoff;
|
||||
IndexTuple it;
|
||||
RTreePageOpaque po;
|
||||
RTreeScanOpaque so;
|
||||
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
/*
|
||||
* If we modified the index during the scan, we may have a pointer to
|
||||
* a ghost tuple, before the scan. If this is the case, back up one.
|
||||
*/
|
||||
|
||||
if (so->s_flags & RTS_CURBEFORE) {
|
||||
so->s_flags &= ~RTS_CURBEFORE;
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
|
||||
while (n >= FirstOffsetNumber && n <= maxoff) {
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
if (po->flags & F_LEAF) {
|
||||
if (index_keytest(it,
|
||||
RelationGetTupleDescriptor(s->relation),
|
||||
s->numberOfKeys, s->keyData))
|
||||
break;
|
||||
} else {
|
||||
if (index_keytest(it,
|
||||
RelationGetTupleDescriptor(s->relation),
|
||||
so->s_internalNKey, so->s_internalKey))
|
||||
break;
|
||||
OffsetNumber maxoff;
|
||||
IndexTuple it;
|
||||
RTreePageOpaque po;
|
||||
RTreeScanOpaque so;
|
||||
|
||||
maxoff = PageGetMaxOffsetNumber(p);
|
||||
po = (RTreePageOpaque) PageGetSpecialPointer(p);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
/*
|
||||
* If we modified the index during the scan, we may have a pointer to
|
||||
* a ghost tuple, before the scan. If this is the case, back up one.
|
||||
*/
|
||||
|
||||
if (so->s_flags & RTS_CURBEFORE)
|
||||
{
|
||||
so->s_flags &= ~RTS_CURBEFORE;
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
|
||||
if (ScanDirectionIsBackward(dir)) {
|
||||
n = OffsetNumberPrev(n);
|
||||
} else {
|
||||
n = OffsetNumberNext(n);
|
||||
|
||||
while (n >= FirstOffsetNumber && n <= maxoff)
|
||||
{
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
if (po->flags & F_LEAF)
|
||||
{
|
||||
if (index_keytest(it,
|
||||
RelationGetTupleDescriptor(s->relation),
|
||||
s->numberOfKeys, s->keyData))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index_keytest(it,
|
||||
RelationGetTupleDescriptor(s->relation),
|
||||
so->s_internalNKey, so->s_internalKey))
|
||||
break;
|
||||
}
|
||||
|
||||
if (ScanDirectionIsBackward(dir))
|
||||
{
|
||||
n = OffsetNumberPrev(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = OffsetNumberNext(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (n);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
static RetrieveIndexResult
|
||||
static RetrieveIndexResult
|
||||
rtscancache(IndexScanDesc s, ScanDirection dir)
|
||||
{
|
||||
RetrieveIndexResult res;
|
||||
ItemPointer ip;
|
||||
|
||||
if (!(ScanDirectionIsNoMovement(dir)
|
||||
&& ItemPointerIsValid(&(s->currentItemData)))) {
|
||||
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
|
||||
ip = rtheapptr(s->relation, &(s->currentItemData));
|
||||
|
||||
if (ItemPointerIsValid(ip))
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
|
||||
else
|
||||
res = (RetrieveIndexResult) NULL;
|
||||
|
||||
pfree (ip);
|
||||
RetrieveIndexResult res;
|
||||
ItemPointer ip;
|
||||
|
||||
return (res);
|
||||
if (!(ScanDirectionIsNoMovement(dir)
|
||||
&& ItemPointerIsValid(&(s->currentItemData))))
|
||||
{
|
||||
|
||||
return ((RetrieveIndexResult) NULL);
|
||||
}
|
||||
|
||||
ip = rtheapptr(s->relation, &(s->currentItemData));
|
||||
|
||||
if (ItemPointerIsValid(ip))
|
||||
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
|
||||
else
|
||||
res = (RetrieveIndexResult) NULL;
|
||||
|
||||
pfree(ip);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* rtheapptr returns the item pointer to the tuple in the heap relation
|
||||
* for which itemp is the index relation item pointer.
|
||||
* rtheapptr returns the item pointer to the tuple in the heap relation
|
||||
* for which itemp is the index relation item pointer.
|
||||
*/
|
||||
static ItemPointer
|
||||
static ItemPointer
|
||||
rtheapptr(Relation r, ItemPointer itemp)
|
||||
{
|
||||
Buffer b;
|
||||
Page p;
|
||||
IndexTuple it;
|
||||
ItemPointer ip;
|
||||
OffsetNumber n;
|
||||
|
||||
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
|
||||
if (ItemPointerIsValid(itemp)) {
|
||||
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
|
||||
p = BufferGetPage(b);
|
||||
n = ItemPointerGetOffsetNumber(itemp);
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
memmove((char *) ip, (char *) &(it->t_tid),
|
||||
sizeof(ItemPointerData));
|
||||
ReleaseBuffer(b);
|
||||
} else {
|
||||
ItemPointerSetInvalid(ip);
|
||||
}
|
||||
|
||||
return (ip);
|
||||
Buffer b;
|
||||
Page p;
|
||||
IndexTuple it;
|
||||
ItemPointer ip;
|
||||
OffsetNumber n;
|
||||
|
||||
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
|
||||
if (ItemPointerIsValid(itemp))
|
||||
{
|
||||
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
|
||||
p = BufferGetPage(b);
|
||||
n = ItemPointerGetOffsetNumber(itemp);
|
||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
|
||||
memmove((char *) ip, (char *) &(it->t_tid),
|
||||
sizeof(ItemPointerData));
|
||||
ReleaseBuffer(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPointerSetInvalid(ip);
|
||||
}
|
||||
|
||||
return (ip);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* rtproc.c--
|
||||
* pg_amproc entries for rtrees.
|
||||
* pg_amproc entries for rtrees.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.7 1997/04/22 17:31:23 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.8 1997/09/07 04:39:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -17,136 +17,139 @@
|
||||
#include <utils/builtins.h>
|
||||
#include <utils/geo_decls.h>
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
BOX
|
||||
*rt_box_union(BOX *a, BOX *b)
|
||||
* rt_box_union(BOX * a, BOX * b)
|
||||
{
|
||||
BOX *n;
|
||||
|
||||
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
|
||||
elog(WARN, "Cannot allocate box for union");
|
||||
|
||||
n->high.x = Max(a->high.x, b->high.x);
|
||||
n->high.y = Max(a->high.y, b->high.y);
|
||||
n->low.x = Min(a->low.x, b->low.x);
|
||||
n->low.y = Min(a->low.y, b->low.y);
|
||||
|
||||
return (n);
|
||||
BOX *n;
|
||||
|
||||
if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL)
|
||||
elog(WARN, "Cannot allocate box for union");
|
||||
|
||||
n->high.x = Max(a->high.x, b->high.x);
|
||||
n->high.y = Max(a->high.y, b->high.y);
|
||||
n->low.x = Min(a->low.x, b->low.x);
|
||||
n->low.y = Min(a->low.y, b->low.y);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
BOX *
|
||||
rt_box_inter(BOX *a, BOX *b)
|
||||
BOX *
|
||||
rt_box_inter(BOX * a, BOX * b)
|
||||
{
|
||||
BOX *n;
|
||||
|
||||
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
|
||||
elog(WARN, "Cannot allocate box for union");
|
||||
|
||||
n->high.x = Min(a->high.x, b->high.x);
|
||||
n->high.y = Min(a->high.y, b->high.y);
|
||||
n->low.x = Max(a->low.x, b->low.x);
|
||||
n->low.y = Max(a->low.y, b->low.y);
|
||||
|
||||
if (n->high.x < n->low.x || n->high.y < n->low.y) {
|
||||
pfree(n);
|
||||
return ((BOX *) NULL);
|
||||
}
|
||||
|
||||
return (n);
|
||||
BOX *n;
|
||||
|
||||
if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL)
|
||||
elog(WARN, "Cannot allocate box for union");
|
||||
|
||||
n->high.x = Min(a->high.x, b->high.x);
|
||||
n->high.y = Min(a->high.y, b->high.y);
|
||||
n->low.x = Max(a->low.x, b->low.x);
|
||||
n->low.y = Max(a->low.y, b->low.y);
|
||||
|
||||
if (n->high.x < n->low.x || n->high.y < n->low.y)
|
||||
{
|
||||
pfree(n);
|
||||
return ((BOX *) NULL);
|
||||
}
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
void
|
||||
rt_box_size(BOX *a, float *size)
|
||||
rt_box_size(BOX * a, float *size)
|
||||
{
|
||||
if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y)
|
||||
*size = 0.0;
|
||||
else
|
||||
*size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y));
|
||||
|
||||
return;
|
||||
if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y)
|
||||
*size = 0.0;
|
||||
else
|
||||
*size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* rt_bigbox_size() -- Compute a size for big boxes.
|
||||
* rt_bigbox_size() -- Compute a size for big boxes.
|
||||
*
|
||||
* In an earlier release of the system, this routine did something
|
||||
* different from rt_box_size. We now use floats, rather than ints,
|
||||
* as the return type for the size routine, so we no longer need to
|
||||
* have a special return type for big boxes.
|
||||
* In an earlier release of the system, this routine did something
|
||||
* different from rt_box_size. We now use floats, rather than ints,
|
||||
* as the return type for the size routine, so we no longer need to
|
||||
* have a special return type for big boxes.
|
||||
*/
|
||||
void
|
||||
rt_bigbox_size(BOX *a, float *size)
|
||||
rt_bigbox_size(BOX * a, float *size)
|
||||
{
|
||||
rt_box_size(a, size);
|
||||
rt_box_size(a, size);
|
||||
}
|
||||
|
||||
POLYGON *
|
||||
rt_poly_union(POLYGON *a, POLYGON *b)
|
||||
POLYGON *
|
||||
rt_poly_union(POLYGON * a, POLYGON * b)
|
||||
{
|
||||
POLYGON *p;
|
||||
|
||||
p = (POLYGON *)PALLOCTYPE(POLYGON);
|
||||
|
||||
if (!PointerIsValid(p))
|
||||
elog(WARN, "Cannot allocate polygon for union");
|
||||
|
||||
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
|
||||
p->size = sizeof(POLYGON);
|
||||
p->npts = 0;
|
||||
p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x);
|
||||
p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y);
|
||||
p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x);
|
||||
p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y);
|
||||
return p;
|
||||
POLYGON *p;
|
||||
|
||||
p = (POLYGON *) PALLOCTYPE(POLYGON);
|
||||
|
||||
if (!PointerIsValid(p))
|
||||
elog(WARN, "Cannot allocate polygon for union");
|
||||
|
||||
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
|
||||
p->size = sizeof(POLYGON);
|
||||
p->npts = 0;
|
||||
p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x);
|
||||
p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y);
|
||||
p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x);
|
||||
p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
rt_poly_size(POLYGON *a, float *size)
|
||||
rt_poly_size(POLYGON * a, float *size)
|
||||
{
|
||||
double xdim, ydim;
|
||||
|
||||
size = (float *) palloc(sizeof(float));
|
||||
if (a == (POLYGON *) NULL ||
|
||||
a->boundbox.high.x <= a->boundbox.low.x ||
|
||||
a->boundbox.high.y <= a->boundbox.low.y)
|
||||
*size = 0.0;
|
||||
else {
|
||||
xdim = (a->boundbox.high.x - a->boundbox.low.x);
|
||||
ydim = (a->boundbox.high.y - a->boundbox.low.y);
|
||||
|
||||
*size = (float) (xdim * ydim);
|
||||
}
|
||||
|
||||
return;
|
||||
double xdim,
|
||||
ydim;
|
||||
|
||||
size = (float *) palloc(sizeof(float));
|
||||
if (a == (POLYGON *) NULL ||
|
||||
a->boundbox.high.x <= a->boundbox.low.x ||
|
||||
a->boundbox.high.y <= a->boundbox.low.y)
|
||||
*size = 0.0;
|
||||
else
|
||||
{
|
||||
xdim = (a->boundbox.high.x - a->boundbox.low.x);
|
||||
ydim = (a->boundbox.high.y - a->boundbox.low.y);
|
||||
|
||||
*size = (float) (xdim * ydim);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
POLYGON *
|
||||
rt_poly_inter(POLYGON *a, POLYGON *b)
|
||||
POLYGON *
|
||||
rt_poly_inter(POLYGON * a, POLYGON * b)
|
||||
{
|
||||
POLYGON *p;
|
||||
|
||||
p = (POLYGON *) PALLOCTYPE(POLYGON);
|
||||
|
||||
if (!PointerIsValid(p))
|
||||
elog(WARN, "Cannot allocate polygon for intersection");
|
||||
|
||||
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
|
||||
p->size = sizeof(POLYGON);
|
||||
p->npts = 0;
|
||||
p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x);
|
||||
p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y);
|
||||
p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x);
|
||||
p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y);
|
||||
|
||||
if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y)
|
||||
POLYGON *p;
|
||||
|
||||
p = (POLYGON *) PALLOCTYPE(POLYGON);
|
||||
|
||||
if (!PointerIsValid(p))
|
||||
elog(WARN, "Cannot allocate polygon for intersection");
|
||||
|
||||
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
|
||||
p->size = sizeof(POLYGON);
|
||||
p->npts = 0;
|
||||
p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x);
|
||||
p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y);
|
||||
p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x);
|
||||
p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y);
|
||||
|
||||
if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y)
|
||||
{
|
||||
pfree(p);
|
||||
return ((POLYGON *) NULL);
|
||||
pfree(p);
|
||||
return ((POLYGON *) NULL);
|
||||
}
|
||||
|
||||
return (p);
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,19 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* rtscan.c--
|
||||
* routines to manage scans on index relations
|
||||
* routines to manage scans on index relations
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.10 1997/05/20 10:29:30 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.11 1997/09/07 04:39:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <storage/bufmgr.h>
|
||||
#include <access/genam.h>
|
||||
#include <storage/lmgr.h>
|
||||
@@ -21,377 +21,411 @@
|
||||
#include <access/rtree.h>
|
||||
#include <access/rtstrat.h>
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# include <regex/utils.h>
|
||||
#include <regex/utils.h>
|
||||
#else
|
||||
# include <string.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* routines defined and used here */
|
||||
static void rtregscan(IndexScanDesc s);
|
||||
static void rtdropscan(IndexScanDesc s);
|
||||
static void rtadjone(IndexScanDesc s, int op, BlockNumber blkno,
|
||||
OffsetNumber offnum);
|
||||
static void adjuststack(RTSTACK *stk, BlockNumber blkno,
|
||||
static void rtregscan(IndexScanDesc s);
|
||||
static void rtdropscan(IndexScanDesc s);
|
||||
static void
|
||||
rtadjone(IndexScanDesc s, int op, BlockNumber blkno,
|
||||
OffsetNumber offnum);
|
||||
static void
|
||||
adjuststack(RTSTACK * stk, BlockNumber blkno,
|
||||
OffsetNumber offnum);
|
||||
static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
|
||||
int op, BlockNumber blkno, OffsetNumber offnum);
|
||||
static void
|
||||
adjustiptr(IndexScanDesc s, ItemPointer iptr,
|
||||
int op, BlockNumber blkno, OffsetNumber offnum);
|
||||
|
||||
/*
|
||||
* Whenever we start an rtree scan in a backend, we register it in private
|
||||
* space. Then if the rtree index gets updated, we check all registered
|
||||
* scans and adjust them if the tuple they point at got moved by the
|
||||
* update. We only need to do this in private space, because when we update
|
||||
* an rtree we have a write lock on the tree, so no other process can have
|
||||
* any locks at all on it. A single transaction can have write and read
|
||||
* locks on the same object, so that's why we need to handle this case.
|
||||
* Whenever we start an rtree scan in a backend, we register it in private
|
||||
* space. Then if the rtree index gets updated, we check all registered
|
||||
* scans and adjust them if the tuple they point at got moved by the
|
||||
* update. We only need to do this in private space, because when we update
|
||||
* an rtree we have a write lock on the tree, so no other process can have
|
||||
* any locks at all on it. A single transaction can have write and read
|
||||
* locks on the same object, so that's why we need to handle this case.
|
||||
*/
|
||||
|
||||
typedef struct RTScanListData {
|
||||
IndexScanDesc rtsl_scan;
|
||||
struct RTScanListData *rtsl_next;
|
||||
} RTScanListData;
|
||||
typedef struct RTScanListData
|
||||
{
|
||||
IndexScanDesc rtsl_scan;
|
||||
struct RTScanListData *rtsl_next;
|
||||
} RTScanListData;
|
||||
|
||||
typedef RTScanListData *RTScanList;
|
||||
typedef RTScanListData *RTScanList;
|
||||
|
||||
/* pointer to list of local scans on rtrees */
|
||||
static RTScanList RTScans = (RTScanList) NULL;
|
||||
|
||||
|
||||
IndexScanDesc
|
||||
rtbeginscan(Relation r,
|
||||
bool fromEnd,
|
||||
uint16 nkeys,
|
||||
ScanKey key)
|
||||
bool fromEnd,
|
||||
uint16 nkeys,
|
||||
ScanKey key)
|
||||
{
|
||||
IndexScanDesc s;
|
||||
|
||||
RelationSetLockForRead(r);
|
||||
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
|
||||
rtregscan(s);
|
||||
|
||||
return (s);
|
||||
IndexScanDesc s;
|
||||
|
||||
RelationSetLockForRead(r);
|
||||
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
|
||||
rtregscan(s);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
void
|
||||
rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
|
||||
{
|
||||
RTreeScanOpaque p;
|
||||
RegProcedure internal_proc;
|
||||
int i;
|
||||
|
||||
if (!IndexScanIsValid(s)) {
|
||||
elog(WARN, "rtrescan: invalid scan.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all the pointers.
|
||||
*/
|
||||
|
||||
ItemPointerSetInvalid(&s->previousItemData);
|
||||
ItemPointerSetInvalid(&s->currentItemData);
|
||||
ItemPointerSetInvalid(&s->nextItemData);
|
||||
ItemPointerSetInvalid(&s->previousMarkData);
|
||||
ItemPointerSetInvalid(&s->currentMarkData);
|
||||
ItemPointerSetInvalid(&s->nextMarkData);
|
||||
|
||||
/*
|
||||
* Set flags.
|
||||
*/
|
||||
if (RelationGetNumberOfBlocks(s->relation) == 0) {
|
||||
s->flags = ScanUnmarked;
|
||||
} else if (fromEnd) {
|
||||
s->flags = ScanUnmarked | ScanUncheckedPrevious;
|
||||
} else {
|
||||
s->flags = ScanUnmarked | ScanUncheckedNext;
|
||||
}
|
||||
|
||||
s->scanFromEnd = fromEnd;
|
||||
|
||||
if (s->numberOfKeys > 0) {
|
||||
memmove(s->keyData,
|
||||
key,
|
||||
s->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
if (p != (RTreeScanOpaque) NULL) {
|
||||
freestack(p->s_stack);
|
||||
freestack(p->s_markstk);
|
||||
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
|
||||
p->s_flags = 0x0;
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
RTreeScanOpaque p;
|
||||
RegProcedure internal_proc;
|
||||
int i;
|
||||
|
||||
if (!IndexScanIsValid(s))
|
||||
{
|
||||
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
|
||||
elog(WARN, "rtrescan: invalid scan.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* initialize opaque data */
|
||||
p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData));
|
||||
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
|
||||
p->s_internalNKey = s->numberOfKeys;
|
||||
p->s_flags = 0x0;
|
||||
s->opaque = p;
|
||||
if (s->numberOfKeys > 0) {
|
||||
p->s_internalKey =
|
||||
(ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys);
|
||||
|
||||
/*
|
||||
* Scans on internal pages use different operators than they
|
||||
* do on leaf pages. For example, if the user wants all boxes
|
||||
* that exactly match (x1,y1,x2,y2), then on internal pages
|
||||
* we need to find all boxes that contain (x1,y1,x2,y2).
|
||||
*/
|
||||
|
||||
for (i = 0; i < s->numberOfKeys; i++) {
|
||||
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
|
||||
internal_proc = RTMapOperator(s->relation,
|
||||
s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
ScanKeyEntryInitialize(&(p->s_internalKey[i]),
|
||||
s->keyData[i].sk_flags,
|
||||
s->keyData[i].sk_attno,
|
||||
internal_proc,
|
||||
s->keyData[i].sk_argument);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear all the pointers.
|
||||
*/
|
||||
|
||||
ItemPointerSetInvalid(&s->previousItemData);
|
||||
ItemPointerSetInvalid(&s->currentItemData);
|
||||
ItemPointerSetInvalid(&s->nextItemData);
|
||||
ItemPointerSetInvalid(&s->previousMarkData);
|
||||
ItemPointerSetInvalid(&s->currentMarkData);
|
||||
ItemPointerSetInvalid(&s->nextMarkData);
|
||||
|
||||
/*
|
||||
* Set flags.
|
||||
*/
|
||||
if (RelationGetNumberOfBlocks(s->relation) == 0)
|
||||
{
|
||||
s->flags = ScanUnmarked;
|
||||
}
|
||||
else if (fromEnd)
|
||||
{
|
||||
s->flags = ScanUnmarked | ScanUncheckedPrevious;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->flags = ScanUnmarked | ScanUncheckedNext;
|
||||
}
|
||||
|
||||
s->scanFromEnd = fromEnd;
|
||||
|
||||
if (s->numberOfKeys > 0)
|
||||
{
|
||||
memmove(s->keyData,
|
||||
key,
|
||||
s->numberOfKeys * sizeof(ScanKeyData));
|
||||
}
|
||||
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
if (p != (RTreeScanOpaque) NULL)
|
||||
{
|
||||
freestack(p->s_stack);
|
||||
freestack(p->s_markstk);
|
||||
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
|
||||
p->s_flags = 0x0;
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
{
|
||||
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize opaque data */
|
||||
p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData));
|
||||
p->s_stack = p->s_markstk = (RTSTACK *) NULL;
|
||||
p->s_internalNKey = s->numberOfKeys;
|
||||
p->s_flags = 0x0;
|
||||
s->opaque = p;
|
||||
if (s->numberOfKeys > 0)
|
||||
{
|
||||
p->s_internalKey =
|
||||
(ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys);
|
||||
|
||||
/*
|
||||
* Scans on internal pages use different operators than they
|
||||
* do on leaf pages. For example, if the user wants all boxes
|
||||
* that exactly match (x1,y1,x2,y2), then on internal pages we
|
||||
* need to find all boxes that contain (x1,y1,x2,y2).
|
||||
*/
|
||||
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
{
|
||||
p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
|
||||
internal_proc = RTMapOperator(s->relation,
|
||||
s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
ScanKeyEntryInitialize(&(p->s_internalKey[i]),
|
||||
s->keyData[i].sk_flags,
|
||||
s->keyData[i].sk_attno,
|
||||
internal_proc,
|
||||
s->keyData[i].sk_argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rtmarkpos(IndexScanDesc s)
|
||||
{
|
||||
RTreeScanOpaque p;
|
||||
RTSTACK *o, *n, *tmp;
|
||||
|
||||
s->currentMarkData = s->currentItemData;
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
if (p->s_flags & RTS_CURBEFORE)
|
||||
p->s_flags |= RTS_MRKBEFORE;
|
||||
else
|
||||
p->s_flags &= ~RTS_MRKBEFORE;
|
||||
|
||||
o = (RTSTACK *) NULL;
|
||||
n = p->s_stack;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (RTSTACK *) NULL) {
|
||||
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
tmp->rts_child = n->rts_child;
|
||||
tmp->rts_blk = n->rts_blk;
|
||||
tmp->rts_parent = o;
|
||||
o = tmp;
|
||||
n = n->rts_parent;
|
||||
}
|
||||
|
||||
freestack(p->s_markstk);
|
||||
p->s_markstk = o;
|
||||
RTreeScanOpaque p;
|
||||
RTSTACK *o,
|
||||
*n,
|
||||
*tmp;
|
||||
|
||||
s->currentMarkData = s->currentItemData;
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
if (p->s_flags & RTS_CURBEFORE)
|
||||
p->s_flags |= RTS_MRKBEFORE;
|
||||
else
|
||||
p->s_flags &= ~RTS_MRKBEFORE;
|
||||
|
||||
o = (RTSTACK *) NULL;
|
||||
n = p->s_stack;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (RTSTACK *) NULL)
|
||||
{
|
||||
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
tmp->rts_child = n->rts_child;
|
||||
tmp->rts_blk = n->rts_blk;
|
||||
tmp->rts_parent = o;
|
||||
o = tmp;
|
||||
n = n->rts_parent;
|
||||
}
|
||||
|
||||
freestack(p->s_markstk);
|
||||
p->s_markstk = o;
|
||||
}
|
||||
|
||||
void
|
||||
rtrestrpos(IndexScanDesc s)
|
||||
{
|
||||
RTreeScanOpaque p;
|
||||
RTSTACK *o, *n, *tmp;
|
||||
|
||||
s->currentItemData = s->currentMarkData;
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
if (p->s_flags & RTS_MRKBEFORE)
|
||||
p->s_flags |= RTS_CURBEFORE;
|
||||
else
|
||||
p->s_flags &= ~RTS_CURBEFORE;
|
||||
|
||||
o = (RTSTACK *) NULL;
|
||||
n = p->s_markstk;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (RTSTACK *) NULL) {
|
||||
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
tmp->rts_child = n->rts_child;
|
||||
tmp->rts_blk = n->rts_blk;
|
||||
tmp->rts_parent = o;
|
||||
o = tmp;
|
||||
n = n->rts_parent;
|
||||
}
|
||||
|
||||
freestack(p->s_stack);
|
||||
p->s_stack = o;
|
||||
RTreeScanOpaque p;
|
||||
RTSTACK *o,
|
||||
*n,
|
||||
*tmp;
|
||||
|
||||
s->currentItemData = s->currentMarkData;
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
if (p->s_flags & RTS_MRKBEFORE)
|
||||
p->s_flags |= RTS_CURBEFORE;
|
||||
else
|
||||
p->s_flags &= ~RTS_CURBEFORE;
|
||||
|
||||
o = (RTSTACK *) NULL;
|
||||
n = p->s_markstk;
|
||||
|
||||
/* copy the parent stack from the current item data */
|
||||
while (n != (RTSTACK *) NULL)
|
||||
{
|
||||
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
|
||||
tmp->rts_child = n->rts_child;
|
||||
tmp->rts_blk = n->rts_blk;
|
||||
tmp->rts_parent = o;
|
||||
o = tmp;
|
||||
n = n->rts_parent;
|
||||
}
|
||||
|
||||
freestack(p->s_stack);
|
||||
p->s_stack = o;
|
||||
}
|
||||
|
||||
void
|
||||
rtendscan(IndexScanDesc s)
|
||||
{
|
||||
RTreeScanOpaque p;
|
||||
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
if (p != (RTreeScanOpaque) NULL) {
|
||||
freestack(p->s_stack);
|
||||
freestack(p->s_markstk);
|
||||
pfree (s->opaque);
|
||||
}
|
||||
|
||||
rtdropscan(s);
|
||||
/* XXX don't unset read lock -- two-phase locking */
|
||||
RTreeScanOpaque p;
|
||||
|
||||
p = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
if (p != (RTreeScanOpaque) NULL)
|
||||
{
|
||||
freestack(p->s_stack);
|
||||
freestack(p->s_markstk);
|
||||
pfree(s->opaque);
|
||||
}
|
||||
|
||||
rtdropscan(s);
|
||||
/* XXX don't unset read lock -- two-phase locking */
|
||||
}
|
||||
|
||||
static void
|
||||
rtregscan(IndexScanDesc s)
|
||||
{
|
||||
RTScanList l;
|
||||
|
||||
l = (RTScanList) palloc(sizeof(RTScanListData));
|
||||
l->rtsl_scan = s;
|
||||
l->rtsl_next = RTScans;
|
||||
RTScans = l;
|
||||
RTScanList l;
|
||||
|
||||
l = (RTScanList) palloc(sizeof(RTScanListData));
|
||||
l->rtsl_scan = s;
|
||||
l->rtsl_next = RTScans;
|
||||
RTScans = l;
|
||||
}
|
||||
|
||||
static void
|
||||
rtdropscan(IndexScanDesc s)
|
||||
{
|
||||
RTScanList l;
|
||||
RTScanList prev;
|
||||
|
||||
prev = (RTScanList) NULL;
|
||||
|
||||
for (l = RTScans;
|
||||
l != (RTScanList) NULL && l->rtsl_scan != s;
|
||||
l = l->rtsl_next) {
|
||||
prev = l;
|
||||
}
|
||||
|
||||
if (l == (RTScanList) NULL)
|
||||
elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s);
|
||||
|
||||
if (prev == (RTScanList) NULL)
|
||||
RTScans = l->rtsl_next;
|
||||
else
|
||||
prev->rtsl_next = l->rtsl_next;
|
||||
|
||||
pfree(l);
|
||||
RTScanList l;
|
||||
RTScanList prev;
|
||||
|
||||
prev = (RTScanList) NULL;
|
||||
|
||||
for (l = RTScans;
|
||||
l != (RTScanList) NULL && l->rtsl_scan != s;
|
||||
l = l->rtsl_next)
|
||||
{
|
||||
prev = l;
|
||||
}
|
||||
|
||||
if (l == (RTScanList) NULL)
|
||||
elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s);
|
||||
|
||||
if (prev == (RTScanList) NULL)
|
||||
RTScans = l->rtsl_next;
|
||||
else
|
||||
prev->rtsl_next = l->rtsl_next;
|
||||
|
||||
pfree(l);
|
||||
}
|
||||
|
||||
void
|
||||
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
|
||||
{
|
||||
RTScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = r->rd_id;
|
||||
for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next) {
|
||||
if (l->rtsl_scan->relation->rd_id == relid)
|
||||
rtadjone(l->rtsl_scan, op, blkno, offnum);
|
||||
}
|
||||
RTScanList l;
|
||||
Oid relid;
|
||||
|
||||
relid = r->rd_id;
|
||||
for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next)
|
||||
{
|
||||
if (l->rtsl_scan->relation->rd_id == relid)
|
||||
rtadjone(l->rtsl_scan, op, blkno, offnum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rtadjone() -- adjust one scan for update.
|
||||
* rtadjone() -- adjust one scan for update.
|
||||
*
|
||||
* By here, the scan passed in is on a modified relation. Op tells
|
||||
* us what the modification is, and blkno and offind tell us what
|
||||
* block and offset index were affected. This routine checks the
|
||||
* current and marked positions, and the current and marked stacks,
|
||||
* to see if any stored location needs to be changed because of the
|
||||
* update. If so, we make the change here.
|
||||
* By here, the scan passed in is on a modified relation. Op tells
|
||||
* us what the modification is, and blkno and offind tell us what
|
||||
* block and offset index were affected. This routine checks the
|
||||
* current and marked positions, and the current and marked stacks,
|
||||
* to see if any stored location needs to be changed because of the
|
||||
* update. If so, we make the change here.
|
||||
*/
|
||||
static void
|
||||
rtadjone(IndexScanDesc s,
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
{
|
||||
RTreeScanOpaque so;
|
||||
|
||||
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
|
||||
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
|
||||
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
if (op == RTOP_SPLIT) {
|
||||
adjuststack(so->s_stack, blkno, offnum);
|
||||
adjuststack(so->s_markstk, blkno, offnum);
|
||||
}
|
||||
RTreeScanOpaque so;
|
||||
|
||||
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
|
||||
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
|
||||
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
if (op == RTOP_SPLIT)
|
||||
{
|
||||
adjuststack(so->s_stack, blkno, offnum);
|
||||
adjuststack(so->s_markstk, blkno, offnum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adjustiptr() -- adjust current and marked item pointers in the scan
|
||||
* adjustiptr() -- adjust current and marked item pointers in the scan
|
||||
*
|
||||
* Depending on the type of update and the place it happened, we
|
||||
* need to do nothing, to back up one record, or to start over on
|
||||
* the same page.
|
||||
* Depending on the type of update and the place it happened, we
|
||||
* need to do nothing, to back up one record, or to start over on
|
||||
* the same page.
|
||||
*/
|
||||
static void
|
||||
adjustiptr(IndexScanDesc s,
|
||||
ItemPointer iptr,
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
ItemPointer iptr,
|
||||
int op,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
{
|
||||
OffsetNumber curoff;
|
||||
RTreeScanOpaque so;
|
||||
|
||||
if (ItemPointerIsValid(iptr)) {
|
||||
if (ItemPointerGetBlockNumber(iptr) == blkno) {
|
||||
curoff = ItemPointerGetOffsetNumber(iptr);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
switch (op) {
|
||||
case RTOP_DEL:
|
||||
/* back up one if we need to */
|
||||
if (curoff >= offnum) {
|
||||
|
||||
if (curoff > FirstOffsetNumber) {
|
||||
/* just adjust the item pointer */
|
||||
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
|
||||
} else {
|
||||
/* remember that we're before the current tuple */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags |= RTS_CURBEFORE;
|
||||
else
|
||||
so->s_flags |= RTS_MRKBEFORE;
|
||||
}
|
||||
OffsetNumber curoff;
|
||||
RTreeScanOpaque so;
|
||||
|
||||
if (ItemPointerIsValid(iptr))
|
||||
{
|
||||
if (ItemPointerGetBlockNumber(iptr) == blkno)
|
||||
{
|
||||
curoff = ItemPointerGetOffsetNumber(iptr);
|
||||
so = (RTreeScanOpaque) s->opaque;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case RTOP_DEL:
|
||||
/* back up one if we need to */
|
||||
if (curoff >= offnum)
|
||||
{
|
||||
|
||||
if (curoff > FirstOffsetNumber)
|
||||
{
|
||||
/* just adjust the item pointer */
|
||||
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* remember that we're before the current tuple */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags |= RTS_CURBEFORE;
|
||||
else
|
||||
so->s_flags |= RTS_MRKBEFORE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTOP_SPLIT:
|
||||
/* back to start of page on split */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags &= ~RTS_CURBEFORE;
|
||||
else
|
||||
so->s_flags &= ~RTS_MRKBEFORE;
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "Bad operation in rtree scan adjust: %d", op);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTOP_SPLIT:
|
||||
/* back to start of page on split */
|
||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
|
||||
if (iptr == &(s->currentItemData))
|
||||
so->s_flags &= ~RTS_CURBEFORE;
|
||||
else
|
||||
so->s_flags &= ~RTS_MRKBEFORE;
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "Bad operation in rtree scan adjust: %d", op);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adjuststack() -- adjust the supplied stack for a split on a page in
|
||||
* the index we're scanning.
|
||||
* adjuststack() -- adjust the supplied stack for a split on a page in
|
||||
* the index we're scanning.
|
||||
*
|
||||
* If a page on our parent stack has split, we need to back up to the
|
||||
* beginning of the page and rescan it. The reason for this is that
|
||||
* the split algorithm for rtrees doesn't order tuples in any useful
|
||||
* way on a single page. This means on that a split, we may wind up
|
||||
* looking at some heap tuples more than once. This is handled in the
|
||||
* access method update code for heaps; if we've modified the tuple we
|
||||
* are looking at already in this transaction, we ignore the update
|
||||
* request.
|
||||
* If a page on our parent stack has split, we need to back up to the
|
||||
* beginning of the page and rescan it. The reason for this is that
|
||||
* the split algorithm for rtrees doesn't order tuples in any useful
|
||||
* way on a single page. This means on that a split, we may wind up
|
||||
* looking at some heap tuples more than once. This is handled in the
|
||||
* access method update code for heaps; if we've modified the tuple we
|
||||
* are looking at already in this transaction, we ignore the update
|
||||
* request.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
adjuststack(RTSTACK *stk,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
adjuststack(RTSTACK * stk,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offnum)
|
||||
{
|
||||
while (stk != (RTSTACK *) NULL) {
|
||||
if (stk->rts_blk == blkno)
|
||||
stk->rts_child = FirstOffsetNumber;
|
||||
|
||||
stk = stk->rts_parent;
|
||||
}
|
||||
while (stk != (RTSTACK *) NULL)
|
||||
{
|
||||
if (stk->rts_blk == blkno)
|
||||
stk->rts_child = FirstOffsetNumber;
|
||||
|
||||
stk = stk->rts_parent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,241 +1,243 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* rtstrat.c--
|
||||
* strategy map data for rtrees.
|
||||
* strategy map data for rtrees.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.6 1997/08/19 21:29:52 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.7 1997/09/07 04:39:26 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
|
||||
#include <utils/rel.h>
|
||||
#include <access/rtree.h>
|
||||
#include <access/istrat.h>
|
||||
|
||||
static StrategyNumber RelationGetRTStrategy(Relation r,
|
||||
AttrNumber attnum, RegProcedure proc);
|
||||
static StrategyNumber
|
||||
RelationGetRTStrategy(Relation r,
|
||||
AttrNumber attnum, RegProcedure proc);
|
||||
|
||||
/*
|
||||
* Note: negate, commute, and negatecommute all assume that operators are
|
||||
* ordered as follows in the strategy map:
|
||||
* Note: negate, commute, and negatecommute all assume that operators are
|
||||
* ordered as follows in the strategy map:
|
||||
*
|
||||
* left, left-or-overlap, overlap, right-or-overlap, right, same,
|
||||
* contains, contained-by
|
||||
* left, left-or-overlap, overlap, right-or-overlap, right, same,
|
||||
* contains, contained-by
|
||||
*
|
||||
* The negate, commute, and negatecommute arrays are used by the planner
|
||||
* to plan indexed scans over data that appears in the qualificiation in
|
||||
* a boolean negation, or whose operands appear in the wrong order. For
|
||||
* example, if the operator "<%" means "contains", and the user says
|
||||
* The negate, commute, and negatecommute arrays are used by the planner
|
||||
* to plan indexed scans over data that appears in the qualificiation in
|
||||
* a boolean negation, or whose operands appear in the wrong order. For
|
||||
* example, if the operator "<%" means "contains", and the user says
|
||||
*
|
||||
* where not rel.box <% "(10,10,20,20)"::box
|
||||
* where not rel.box <% "(10,10,20,20)"::box
|
||||
*
|
||||
* the planner can plan an index scan by noting that rtree indices have
|
||||
* an operator in their operator class for negating <%.
|
||||
* the planner can plan an index scan by noting that rtree indices have
|
||||
* an operator in their operator class for negating <%.
|
||||
*
|
||||
* Similarly, if the user says something like
|
||||
* Similarly, if the user says something like
|
||||
*
|
||||
* where "(10,10,20,20)"::box <% rel.box
|
||||
* where "(10,10,20,20)"::box <% rel.box
|
||||
*
|
||||
* the planner can see that the rtree index on rel.box has an operator in
|
||||
* its opclass for commuting <%, and plan the scan using that operator.
|
||||
* This added complexity in the access methods makes the planner a lot easier
|
||||
* to write.
|
||||
* the planner can see that the rtree index on rel.box has an operator in
|
||||
* its opclass for commuting <%, and plan the scan using that operator.
|
||||
* This added complexity in the access methods makes the planner a lot easier
|
||||
* to write.
|
||||
*/
|
||||
|
||||
/* if a op b, what operator tells us if (not a op b)? */
|
||||
static StrategyNumber RTNegate[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
static StrategyNumber RTNegate[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/* if a op_1 b, what is the operator op_2 such that b op_2 a? */
|
||||
static StrategyNumber RTCommute[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
static StrategyNumber RTCommute[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
|
||||
static StrategyNumber RTNegateCommute[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/*
|
||||
* Now do the TermData arrays. These exist in case the user doesn't give
|
||||
* us a full set of operators for a particular operator class. The idea
|
||||
* is that by making multiple comparisons using any one of the supplied
|
||||
* operators, we can decide whether two n-dimensional polygons are equal.
|
||||
* For example, if a contains b and b contains a, we may conclude that
|
||||
* a and b are equal.
|
||||
*
|
||||
* The presence of the TermData arrays in all this is a historical accident.
|
||||
* Early in the development of the POSTGRES access methods, it was believed
|
||||
* that writing functions was harder than writing arrays. This is wrong;
|
||||
* TermData is hard to understand and hard to get right. In general, when
|
||||
* someone populates a new operator class, the populate it completely. If
|
||||
* Mike Hirohama had forced Cimarron Taylor to populate the strategy map
|
||||
* for btree int2_ops completely in 1988, you wouldn't have to deal with
|
||||
* all this now. Too bad for you.
|
||||
*
|
||||
* Since you can't necessarily do this in all cases (for example, you can't
|
||||
* do it given only "intersects" or "disjoint"), TermData arrays for some
|
||||
* operators don't appear below.
|
||||
*
|
||||
* Note that if you DO supply all the operators required in a given opclass
|
||||
* by inserting them into the pg_opclass system catalog, you can get away
|
||||
* without doing all this TermData stuff. Since the rtree code is intended
|
||||
* to be a reference for access method implementors, I'm doing TermData
|
||||
* correctly here.
|
||||
*
|
||||
* Note on style: these are all actually of type StrategyTermData, but
|
||||
* since those have variable-length data at the end of the struct we can't
|
||||
* properly initialize them if we declare them to be what they are.
|
||||
*/
|
||||
|
||||
/* if you only have "contained-by", how do you determine equality? */
|
||||
static uint16 RTContainedByTermData[] = {
|
||||
2, /* make two comparisons */
|
||||
RTContainedByStrategyNumber, /* use "a contained-by b" */
|
||||
0x0, /* without any magic */
|
||||
RTContainedByStrategyNumber, /* then use contained-by, */
|
||||
SK_COMMUTE /* swapping a and b */
|
||||
};
|
||||
|
||||
/* if you only have "contains", how do you determine equality? */
|
||||
static uint16 RTContainsTermData[] = {
|
||||
2, /* make two comparisons */
|
||||
RTContainsStrategyNumber, /* use "a contains b" */
|
||||
0x0, /* without any magic */
|
||||
RTContainsStrategyNumber, /* then use contains again, */
|
||||
SK_COMMUTE /* swapping a and b */
|
||||
};
|
||||
|
||||
/* now put all that together in one place for the planner */
|
||||
static StrategyTerm RTEqualExpressionData[] = {
|
||||
(StrategyTerm) RTContainedByTermData,
|
||||
(StrategyTerm) RTContainsTermData,
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* If you were sufficiently attentive to detail, you would go through
|
||||
* the ExpressionData pain above for every one of the seven strategies
|
||||
* we defined. I am not. Now we declare the StrategyEvaluationData
|
||||
* structure that gets shipped around to help the planner and the access
|
||||
* method decide what sort of scan it should do, based on (a) what the
|
||||
* user asked for, (b) what operators are defined for a particular opclass,
|
||||
* and (c) the reams of information we supplied above.
|
||||
*
|
||||
* The idea of all of this initialized data is to make life easier on the
|
||||
* user when he defines a new operator class to use this access method.
|
||||
* By filling in all the data, we let him get away with leaving holes in his
|
||||
* operator class, and still let him use the index. The added complexity
|
||||
* in the access methods just isn't worth the trouble, though.
|
||||
*/
|
||||
|
||||
static StrategyEvaluationData RTEvaluationData = {
|
||||
RTNStrategies, /* # of strategies */
|
||||
(StrategyTransformMap) RTNegate, /* how to do (not qual) */
|
||||
(StrategyTransformMap) RTCommute, /* how to swap operands */
|
||||
(StrategyTransformMap) RTNegateCommute, /* how to do both */
|
||||
{
|
||||
NULL, /* express left */
|
||||
NULL, /* express overleft */
|
||||
NULL, /* express over */
|
||||
NULL, /* express overright */
|
||||
NULL, /* express right */
|
||||
(StrategyExpression) RTEqualExpressionData, /* express same */
|
||||
NULL, /* express contains */
|
||||
NULL, /* express contained-by */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
}
|
||||
static StrategyNumber RTNegateCommute[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/*
|
||||
* Okay, now something peculiar to rtrees that doesn't apply to most other
|
||||
* indexing structures: When we're searching a tree for a given value, we
|
||||
* can't do the same sorts of comparisons on internal node entries as we
|
||||
* do at leaves. The reason is that if we're looking for (say) all boxes
|
||||
* that are the same as (0,0,10,10), then we need to find all leaf pages
|
||||
* that overlap that region. So internally we search for overlap, and at
|
||||
* the leaf we search for equality.
|
||||
* Now do the TermData arrays. These exist in case the user doesn't give
|
||||
* us a full set of operators for a particular operator class. The idea
|
||||
* is that by making multiple comparisons using any one of the supplied
|
||||
* operators, we can decide whether two n-dimensional polygons are equal.
|
||||
* For example, if a contains b and b contains a, we may conclude that
|
||||
* a and b are equal.
|
||||
*
|
||||
* This array maps leaf search operators to the internal search operators.
|
||||
* We assume the normal ordering on operators:
|
||||
* The presence of the TermData arrays in all this is a historical accident.
|
||||
* Early in the development of the POSTGRES access methods, it was believed
|
||||
* that writing functions was harder than writing arrays. This is wrong;
|
||||
* TermData is hard to understand and hard to get right. In general, when
|
||||
* someone populates a new operator class, the populate it completely. If
|
||||
* Mike Hirohama had forced Cimarron Taylor to populate the strategy map
|
||||
* for btree int2_ops completely in 1988, you wouldn't have to deal with
|
||||
* all this now. Too bad for you.
|
||||
*
|
||||
* left, left-or-overlap, overlap, right-or-overlap, right, same,
|
||||
* contains, contained-by
|
||||
* Since you can't necessarily do this in all cases (for example, you can't
|
||||
* do it given only "intersects" or "disjoint"), TermData arrays for some
|
||||
* operators don't appear below.
|
||||
*
|
||||
* Note that if you DO supply all the operators required in a given opclass
|
||||
* by inserting them into the pg_opclass system catalog, you can get away
|
||||
* without doing all this TermData stuff. Since the rtree code is intended
|
||||
* to be a reference for access method implementors, I'm doing TermData
|
||||
* correctly here.
|
||||
*
|
||||
* Note on style: these are all actually of type StrategyTermData, but
|
||||
* since those have variable-length data at the end of the struct we can't
|
||||
* properly initialize them if we declare them to be what they are.
|
||||
*/
|
||||
|
||||
/* if you only have "contained-by", how do you determine equality? */
|
||||
static uint16 RTContainedByTermData[] = {
|
||||
2, /* make two comparisons */
|
||||
RTContainedByStrategyNumber,/* use "a contained-by b" */
|
||||
0x0, /* without any magic */
|
||||
RTContainedByStrategyNumber,/* then use contained-by, */
|
||||
SK_COMMUTE /* swapping a and b */
|
||||
};
|
||||
|
||||
/* if you only have "contains", how do you determine equality? */
|
||||
static uint16 RTContainsTermData[] = {
|
||||
2, /* make two comparisons */
|
||||
RTContainsStrategyNumber, /* use "a contains b" */
|
||||
0x0, /* without any magic */
|
||||
RTContainsStrategyNumber, /* then use contains again, */
|
||||
SK_COMMUTE /* swapping a and b */
|
||||
};
|
||||
|
||||
/* now put all that together in one place for the planner */
|
||||
static StrategyTerm RTEqualExpressionData[] = {
|
||||
(StrategyTerm) RTContainedByTermData,
|
||||
(StrategyTerm) RTContainsTermData,
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* If you were sufficiently attentive to detail, you would go through
|
||||
* the ExpressionData pain above for every one of the seven strategies
|
||||
* we defined. I am not. Now we declare the StrategyEvaluationData
|
||||
* structure that gets shipped around to help the planner and the access
|
||||
* method decide what sort of scan it should do, based on (a) what the
|
||||
* user asked for, (b) what operators are defined for a particular opclass,
|
||||
* and (c) the reams of information we supplied above.
|
||||
*
|
||||
* The idea of all of this initialized data is to make life easier on the
|
||||
* user when he defines a new operator class to use this access method.
|
||||
* By filling in all the data, we let him get away with leaving holes in his
|
||||
* operator class, and still let him use the index. The added complexity
|
||||
* in the access methods just isn't worth the trouble, though.
|
||||
*/
|
||||
|
||||
static StrategyEvaluationData RTEvaluationData = {
|
||||
RTNStrategies, /* # of strategies */
|
||||
(StrategyTransformMap) RTNegate, /* how to do (not qual) */
|
||||
(StrategyTransformMap) RTCommute, /* how to swap operands */
|
||||
(StrategyTransformMap) RTNegateCommute, /* how to do both */
|
||||
{
|
||||
NULL, /* express left */
|
||||
NULL, /* express overleft */
|
||||
NULL, /* express over */
|
||||
NULL, /* express overright */
|
||||
NULL, /* express right */
|
||||
(StrategyExpression) RTEqualExpressionData, /* express same */
|
||||
NULL, /* express contains */
|
||||
NULL, /* express contained-by */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Okay, now something peculiar to rtrees that doesn't apply to most other
|
||||
* indexing structures: When we're searching a tree for a given value, we
|
||||
* can't do the same sorts of comparisons on internal node entries as we
|
||||
* do at leaves. The reason is that if we're looking for (say) all boxes
|
||||
* that are the same as (0,0,10,10), then we need to find all leaf pages
|
||||
* that overlap that region. So internally we search for overlap, and at
|
||||
* the leaf we search for equality.
|
||||
*
|
||||
* This array maps leaf search operators to the internal search operators.
|
||||
* We assume the normal ordering on operators:
|
||||
*
|
||||
* left, left-or-overlap, overlap, right-or-overlap, right, same,
|
||||
* contains, contained-by
|
||||
*/
|
||||
static StrategyNumber RTOperMap[RTNStrategies] = {
|
||||
RTOverLeftStrategyNumber,
|
||||
RTOverLeftStrategyNumber,
|
||||
RTOverlapStrategyNumber,
|
||||
RTOverRightStrategyNumber,
|
||||
RTOverRightStrategyNumber,
|
||||
RTContainsStrategyNumber,
|
||||
RTContainsStrategyNumber,
|
||||
RTOverlapStrategyNumber
|
||||
};
|
||||
RTOverLeftStrategyNumber,
|
||||
RTOverLeftStrategyNumber,
|
||||
RTOverlapStrategyNumber,
|
||||
RTOverRightStrategyNumber,
|
||||
RTOverRightStrategyNumber,
|
||||
RTContainsStrategyNumber,
|
||||
RTContainsStrategyNumber,
|
||||
RTOverlapStrategyNumber
|
||||
};
|
||||
|
||||
static StrategyNumber
|
||||
static StrategyNumber
|
||||
RelationGetRTStrategy(Relation r,
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
{
|
||||
return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc));
|
||||
return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc));
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
bool
|
||||
RelationInvokeRTStrategy(Relation r,
|
||||
AttrNumber attnum,
|
||||
StrategyNumber s,
|
||||
Datum left,
|
||||
Datum right)
|
||||
AttrNumber attnum,
|
||||
StrategyNumber s,
|
||||
Datum left,
|
||||
Datum right)
|
||||
{
|
||||
return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
|
||||
left, right));
|
||||
return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
|
||||
left, right));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
RegProcedure
|
||||
RTMapOperator(Relation r,
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
{
|
||||
StrategyNumber procstrat;
|
||||
StrategyMap strategyMap;
|
||||
|
||||
procstrat = RelationGetRTStrategy(r, attnum, proc);
|
||||
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
|
||||
RTNStrategies,
|
||||
attnum);
|
||||
|
||||
return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
|
||||
StrategyNumber procstrat;
|
||||
StrategyMap strategyMap;
|
||||
|
||||
procstrat = RelationGetRTStrategy(r, attnum, proc);
|
||||
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
|
||||
RTNStrategies,
|
||||
attnum);
|
||||
|
||||
return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* xid.c--
|
||||
* POSTGRES transaction identifier code.
|
||||
* POSTGRES transaction identifier code.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.7 1997/08/19 21:30:20 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.8 1997/09/07 04:39:40 momjian Exp $
|
||||
*
|
||||
* OLD COMMENTS
|
||||
* XXX WARNING
|
||||
* Much of this file will change when we change our representation
|
||||
* of transaction ids -cim 3/23/90
|
||||
* Much of this file will change when we change our representation
|
||||
* of transaction ids -cim 3/23/90
|
||||
*
|
||||
* It is time to make the switch from 5 byte to 4 byte transaction ids
|
||||
* This file was totally reworked. -mer 5/22/92
|
||||
@@ -31,127 +31,127 @@ extern TransactionId AmiTransactionId;
|
||||
extern TransactionId FirstTransactionId;
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TransactionIdIsValid
|
||||
* TransactionIdIsValid
|
||||
*
|
||||
* Macro-ize me.
|
||||
* Macro-ize me.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
TransactionIdIsValid(TransactionId transactionId)
|
||||
{
|
||||
return ((bool) (transactionId != NullTransactionId) );
|
||||
return ((bool) (transactionId != NullTransactionId));
|
||||
}
|
||||
|
||||
/* XXX char16 name for catalogs */
|
||||
TransactionId
|
||||
xidin(char *representation)
|
||||
{
|
||||
return (atol(representation));
|
||||
return (atol(representation));
|
||||
}
|
||||
|
||||
/* XXX char16 name for catalogs */
|
||||
char*
|
||||
char *
|
||||
xidout(TransactionId transactionId)
|
||||
{
|
||||
/* return(TransactionIdFormString(transactionId)); */
|
||||
char *representation;
|
||||
|
||||
/* maximum 32 bit unsigned integer representation takes 10 chars */
|
||||
representation = palloc(11);
|
||||
|
||||
sprintf(representation, "%u", transactionId);
|
||||
|
||||
return (representation);
|
||||
/* return(TransactionIdFormString(transactionId)); */
|
||||
char *representation;
|
||||
|
||||
/* maximum 32 bit unsigned integer representation takes 10 chars */
|
||||
representation = palloc(11);
|
||||
|
||||
sprintf(representation, "%u", transactionId);
|
||||
|
||||
return (representation);
|
||||
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* StoreInvalidTransactionId
|
||||
* StoreInvalidTransactionId
|
||||
*
|
||||
* Maybe do away with Pointer types in these routines.
|
||||
* Macro-ize this one.
|
||||
* Maybe do away with Pointer types in these routines.
|
||||
* Macro-ize this one.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
StoreInvalidTransactionId(TransactionId *destination)
|
||||
StoreInvalidTransactionId(TransactionId * destination)
|
||||
{
|
||||
*destination = NullTransactionId;
|
||||
*destination = NullTransactionId;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TransactionIdStore
|
||||
* TransactionIdStore
|
||||
*
|
||||
* Macro-ize this one.
|
||||
* Macro-ize this one.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
TransactionIdStore(TransactionId transactionId,
|
||||
TransactionId *destination)
|
||||
TransactionId * destination)
|
||||
{
|
||||
*destination = transactionId;
|
||||
*destination = transactionId;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TransactionIdEquals
|
||||
* TransactionIdEquals
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
TransactionIdEquals(TransactionId id1, TransactionId id2)
|
||||
{
|
||||
return ((bool) (id1 == id2));
|
||||
return ((bool) (id1 == id2));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TransactionIdIsLessThan
|
||||
* TransactionIdIsLessThan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
TransactionIdIsLessThan(TransactionId id1, TransactionId id2)
|
||||
{
|
||||
return ((bool)(id1 < id2));
|
||||
return ((bool) (id1 < id2));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* xideq
|
||||
* xideq
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* xideq - returns 1, iff xid1 == xid2
|
||||
* 0 else;
|
||||
* xideq - returns 1, iff xid1 == xid2
|
||||
* 0 else;
|
||||
*/
|
||||
bool
|
||||
xideq(TransactionId xid1, TransactionId xid2)
|
||||
{
|
||||
return( (bool) (xid1 == xid2) );
|
||||
return ((bool) (xid1 == xid2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TransactionIdIncrement
|
||||
* TransactionIdIncrement
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
TransactionIdIncrement(TransactionId *transactionId)
|
||||
TransactionIdIncrement(TransactionId * transactionId)
|
||||
{
|
||||
|
||||
(*transactionId)++;
|
||||
if (*transactionId == DisabledTransactionId)
|
||||
elog(FATAL, "TransactionIdIncrement: exhausted XID's");
|
||||
return;
|
||||
|
||||
(*transactionId)++;
|
||||
if (*transactionId == DisabledTransactionId)
|
||||
elog(FATAL, "TransactionIdIncrement: exhausted XID's");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* TransactionIdAdd
|
||||
* TransactionIdAdd
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
TransactionIdAdd(TransactionId *xid, int value)
|
||||
TransactionIdAdd(TransactionId * xid, int value)
|
||||
{
|
||||
*xid += value;
|
||||
return;
|
||||
*xid += value;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user