|
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* genam.c
|
|
|
|
|
* general index access method routines
|
|
|
|
|
*
|
|
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* IDENTIFICATION
|
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.32 2002/03/29 22:10:32 tgl Exp $
|
|
|
|
|
*
|
|
|
|
|
* NOTES
|
|
|
|
|
* many of the old access method routines have been turned into
|
|
|
|
|
* macros and moved to genam.h -cim 4/30/91
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
* OLD COMMENTS
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* 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)
|
|
|
|
|
*
|
|
|
|
|
* All other states cannot occur.
|
|
|
|
|
*
|
|
|
|
|
* 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/heapam.h"
|
|
|
|
|
#include "miscadmin.h"
|
|
|
|
|
#include "pgstat.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* The intent is that an AM implementor will define a beginscan routine
|
|
|
|
|
* that calls RelationGetIndexScan, to fill in the scan, and then does
|
|
|
|
|
* whatever kind of locking he wants.
|
|
|
|
|
*
|
|
|
|
|
* At the end of a scan, the AM's endscan routine undoes the locking,
|
|
|
|
|
* but does *not* call IndexScanEnd --- the higher-level index_endscan
|
|
|
|
|
* routine does that. (We can't do it in the AM because index_endscan
|
|
|
|
|
* still needs to touch the IndexScanDesc after calling the AM.)
|
|
|
|
|
*
|
|
|
|
|
* Because of this, the AM does not have a choice whether to call
|
|
|
|
|
* RelationGetIndexScan or not; its beginscan routine must return an
|
|
|
|
|
* object made by RelationGetIndexScan. This is kinda ugly but not
|
|
|
|
|
* worth cleaning up now.
|
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Parameters:
|
|
|
|
|
* relation -- index relation for scan.
|
|
|
|
|
* scanFromEnd -- if true, begin scan at one of the index's
|
|
|
|
|
* endpoints.
|
|
|
|
|
* numberOfKeys -- count of scan keys.
|
|
|
|
|
* key -- the ScanKey for the starting position of the scan.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* An initialized IndexScanDesc.
|
|
|
|
|
* ----------------
|
|
|
|
|
*/
|
|
|
|
|
IndexScanDesc
|
|
|
|
|
RelationGetIndexScan(Relation relation,
|
|
|
|
|
bool scanFromEnd,
|
|
|
|
|
uint16 numberOfKeys,
|
|
|
|
|
ScanKey key)
|
|
|
|
|
{
|
|
|
|
|
IndexScanDesc scan;
|
|
|
|
|
|
|
|
|
|
if (!RelationIsValid(relation))
|
|
|
|
|
elog(ERROR, "RelationGetIndexScan: relation invalid");
|
|
|
|
|
|
|
|
|
|
scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
|
|
|
|
|
|
|
|
|
|
scan->relation = relation;
|
|
|
|
|
scan->opaque = NULL;
|
|
|
|
|
scan->numberOfKeys = numberOfKeys;
|
|
|
|
|
|
|
|
|
|
ItemPointerSetInvalid(&scan->currentItemData);
|
|
|
|
|
ItemPointerSetInvalid(&scan->currentMarkData);
|
|
|
|
|
|
|
|
|
|
pgstat_initstats(&scan->xs_pgstat_info, relation);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* mark cached function lookup data invalid; it will be set on first
|
|
|
|
|
* use
|
|
|
|
|
*/
|
|
|
|
|
scan->fn_getnext.fn_oid = InvalidOid;
|
|
|
|
|
|
|
|
|
|
if (numberOfKeys > 0)
|
|
|
|
|
scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
|
|
|
|
|
else
|
|
|
|
|
scan->keyData = NULL;
|
|
|
|
|
|
|
|
|
|
index_rescan(scan, scanFromEnd, key);
|
|
|
|
|
|
|
|
|
|
return scan;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* IndexScanEnd -- End an index scan.
|
|
|
|
|
*
|
|
|
|
|
* This routine just releases the storage acquired by
|
|
|
|
|
* RelationGetIndexScan(). Any AM-level resources are
|
|
|
|
|
* assumed to already have been released by the AM's
|
|
|
|
|
* endscan routine.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
IndexScanEnd(IndexScanDesc scan)
|
|
|
|
|
{
|
|
|
|
|
if (!IndexScanIsValid(scan))
|
|
|
|
|
elog(ERROR, "IndexScanEnd: invalid scan");
|
|
|
|
|
|
|
|
|
|
if (scan->keyData != NULL)
|
|
|
|
|
pfree(scan->keyData);
|
|
|
|
|
|
|
|
|
|
pfree(scan);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef NOT_USED
|
|
|
|
|
/* ----------------
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
IndexScanRestart(IndexScanDesc scan,
|
|
|
|
|
bool scanFromEnd,
|
|
|
|
|
ScanKey key)
|
|
|
|
|
{
|
|
|
|
|
if (!IndexScanIsValid(scan))
|
|
|
|
|
elog(ERROR, "IndexScanRestart: invalid scan");
|
|
|
|
|
|
|
|
|
|
ItemPointerSetInvalid(&scan->currentItemData);
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
IndexScanMarkPosition(IndexScanDesc scan)
|
|
|
|
|
{
|
|
|
|
|
scan->currentMarkData = scan->currentItemData;
|
|
|
|
|
|
|
|
|
|
scan->flags = 0x0; /* XXX should have a symbolic name */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ----------------
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
IndexScanRestorePosition(IndexScanDesc scan)
|
|
|
|
|
{
|
|
|
|
|
if (scan->flags & ScanUnmarked)
|
|
|
|
|
elog(ERROR, "IndexScanRestorePosition: no mark to restore");
|
|
|
|
|
|
|
|
|
|
scan->currentItemData = scan->currentMarkData;
|
|
|
|
|
|
|
|
|
|
scan->flags = 0x0; /* XXX should have a symbolic name */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
|
* heap-or-index-scan access to system catalogs
|
|
|
|
|
*
|
|
|
|
|
* These functions support system catalog accesses that normally use
|
|
|
|
|
* an index but need to be capable of being switched to heap scans
|
|
|
|
|
* if the system indexes are unavailable. The interface is
|
|
|
|
|
* as easy to use as a heap scan, and hides all the extra cruft of
|
|
|
|
|
* the present indexscan API.
|
|
|
|
|
*
|
|
|
|
|
* The specified scan keys must be compatible with the named index.
|
|
|
|
|
* Generally this means that they must constrain either all columns
|
|
|
|
|
* of the index, or the first K columns of an N-column index.
|
|
|
|
|
*
|
|
|
|
|
* These routines would work fine with non-system tables, actually,
|
|
|
|
|
* but they're only useful when there is a known index to use with
|
|
|
|
|
* the given scan keys, so in practice they're only good for
|
|
|
|
|
* predetermined types of scans of system catalogs.
|
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* systable_beginscan --- set up for heap-or-index scan
|
|
|
|
|
*
|
|
|
|
|
* rel: catalog to scan, already opened and suitably locked
|
|
|
|
|
* indexRelname: name of index to conditionally use
|
|
|
|
|
* indexOK: if false, forces a heap scan (see notes below)
|
|
|
|
|
* snapshot: time qual to use (usually should be SnapshotNow)
|
|
|
|
|
* nkeys, key: scan keys
|
|
|
|
|
*
|
|
|
|
|
* The attribute numbers in the scan key should be set for the heap case.
|
|
|
|
|
* If we choose to index, we reset them to 1..n to reference the index
|
|
|
|
|
* columns. Note this means there must be one scankey qualification per
|
|
|
|
|
* index column! This is checked by the Asserts in the normal, index-using
|
|
|
|
|
* case, but won't be checked if the heapscan path is taken.
|
|
|
|
|
*
|
|
|
|
|
* The routine checks the normal cases for whether an indexscan is safe,
|
|
|
|
|
* but caller can make additional checks and pass indexOK=false if needed.
|
|
|
|
|
* In standard case indexOK can simply be constant TRUE.
|
|
|
|
|
*/
|
|
|
|
|
SysScanDesc
|
|
|
|
|
systable_beginscan(Relation rel,
|
|
|
|
|
const char *indexRelname,
|
|
|
|
|
bool indexOK,
|
|
|
|
|
Snapshot snapshot,
|
|
|
|
|
unsigned nkeys, ScanKey key)
|
|
|
|
|
{
|
|
|
|
|
SysScanDesc sysscan;
|
|
|
|
|
|
|
|
|
|
sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
|
|
|
|
|
sysscan->heap_rel = rel;
|
|
|
|
|
sysscan->snapshot = snapshot;
|
|
|
|
|
sysscan->tuple.t_datamcxt = NULL;
|
|
|
|
|
sysscan->tuple.t_data = NULL;
|
|
|
|
|
sysscan->buffer = InvalidBuffer;
|
|
|
|
|
|
|
|
|
|
if (indexOK &&
|
|
|
|
|
rel->rd_rel->relhasindex &&
|
|
|
|
|
!IsIgnoringSystemIndexes())
|
|
|
|
|
{
|
|
|
|
|
Relation irel;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/* We assume it's a system index, so index_openr is OK */
|
|
|
|
|
sysscan->irel = irel = index_openr(indexRelname);
|
|
|
|
|
/*
|
|
|
|
|
* Change attribute numbers to be index column numbers.
|
|
|
|
|
*
|
|
|
|
|
* This code could be generalized to search for the index key numbers
|
|
|
|
|
* to substitute, but for now there's no need.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < nkeys; i++)
|
|
|
|
|
{
|
|
|
|
|
Assert(key[i].sk_attno == irel->rd_index->indkey[i]);
|
|
|
|
|
key[i].sk_attno = i+1;
|
|
|
|
|
}
|
|
|
|
|
sysscan->iscan = index_beginscan(irel, false, nkeys, key);
|
|
|
|
|
sysscan->scan = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sysscan->irel = (Relation) NULL;
|
|
|
|
|
sysscan->scan = heap_beginscan(rel, false, snapshot, nkeys, key);
|
|
|
|
|
sysscan->iscan = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sysscan;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* systable_getnext --- get next tuple in a heap-or-index scan
|
|
|
|
|
*
|
|
|
|
|
* Returns NULL if no more tuples available.
|
|
|
|
|
*
|
|
|
|
|
* Note that returned tuple is a reference to data in a disk buffer;
|
|
|
|
|
* it must not be modified, and should be presumed inaccessible after
|
|
|
|
|
* next getnext() or endscan() call.
|
|
|
|
|
*/
|
|
|
|
|
HeapTuple
|
|
|
|
|
systable_getnext(SysScanDesc sysscan)
|
|
|
|
|
{
|
|
|
|
|
HeapTuple htup = (HeapTuple) NULL;
|
|
|
|
|
|
|
|
|
|
if (sysscan->irel)
|
|
|
|
|
{
|
|
|
|
|
RetrieveIndexResult indexRes;
|
|
|
|
|
|
|
|
|
|
if (BufferIsValid(sysscan->buffer))
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(sysscan->buffer);
|
|
|
|
|
sysscan->buffer = InvalidBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((indexRes = index_getnext(sysscan->iscan, ForwardScanDirection)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
sysscan->tuple.t_self = indexRes->heap_iptr;
|
|
|
|
|
pfree(indexRes);
|
|
|
|
|
heap_fetch(sysscan->heap_rel, sysscan->snapshot,
|
|
|
|
|
&sysscan->tuple, &sysscan->buffer,
|
|
|
|
|
sysscan->iscan);
|
|
|
|
|
if (sysscan->tuple.t_data != NULL)
|
|
|
|
|
{
|
|
|
|
|
htup = &sysscan->tuple;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
htup = heap_getnext(sysscan->scan, 0);
|
|
|
|
|
|
|
|
|
|
return htup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* systable_endscan --- close scan, release resources
|
|
|
|
|
*
|
|
|
|
|
* Note that it's still up to the caller to close the heap relation.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
systable_endscan(SysScanDesc sysscan)
|
|
|
|
|
{
|
|
|
|
|
if (sysscan->irel)
|
|
|
|
|
{
|
|
|
|
|
if (BufferIsValid(sysscan->buffer))
|
|
|
|
|
ReleaseBuffer(sysscan->buffer);
|
|
|
|
|
index_endscan(sysscan->iscan);
|
|
|
|
|
index_close(sysscan->irel);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
heap_endscan(sysscan->scan);
|
|
|
|
|
|
|
|
|
|
pfree(sysscan);
|
|
|
|
|
}
|