postgresql/src/backend/access/gin/ginutil.c

438 lines
9.9 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* ginutil.c
2006-10-04 02:30:14 +02:00
* utilities routines for the postgres inverted index access method.
*
*
2011-01-01 19:18:15 +01:00
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
2010-09-20 22:08:53 +02:00
* src/backend/access/gin/ginutil.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/gin.h"
#include "access/reloptions.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
2006-10-04 02:30:14 +02:00
void
initGinState(GinState *state, Relation index)
{
int i;
state->origTupdesc = index->rd_att;
state->oneCol = (index->rd_att->natts == 1) ? true : false;
for (i = 0; i < index->rd_att->natts; i++)
{
state->tupdesc[i] = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
INT2OID, -1, 0);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
index->rd_att->attrs[i]->atttypid,
index->rd_att->attrs[i]->atttypmod,
index->rd_att->attrs[i]->attndims
);
fmgr_info_copy(&(state->compareFn[i]),
index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->extractValueFn[i]),
index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->extractQueryFn[i]),
index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->consistentFn[i]),
index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
CurrentMemoryContext);
/*
* Check opclass capability to do partial match.
*/
if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
{
fmgr_info_copy(&(state->comparePartialFn[i]),
index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
CurrentMemoryContext);
state->canPartialMatch[i] = true;
}
else
{
state->canPartialMatch[i] = false;
}
}
}
/*
* Extract attribute (column) number of stored entry from GIN tuple
*/
OffsetNumber
gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
{
OffsetNumber colN = FirstOffsetNumber;
if (!ginstate->oneCol)
{
Datum res;
bool isnull;
/*
* First attribute is always int16, so we can safely use any tuple
* descriptor to obtain first attribute of tuple
*/
res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
&isnull);
Assert(!isnull);
colN = DatumGetUInt16(res);
Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
}
return colN;
}
/*
* Extract stored datum from GIN tuple
*/
Datum
gin_index_getattr(GinState *ginstate, IndexTuple tuple)
{
bool isnull;
Datum res;
if (ginstate->oneCol)
{
/*
* Single column index doesn't store attribute numbers in tuples
*/
res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
&isnull);
}
else
{
/*
* Since the datum type depends on which index column it's from, we
* must be careful to use the right tuple descriptor here.
*/
OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);
res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
ginstate->tupdesc[colN - 1],
&isnull);
}
Assert(!isnull);
return res;
}
/*
* Allocate a new page (either by recycling, or by extending the index file)
* The returned buffer is already pinned and exclusive-locked
* Caller is responsible for initializing the page by calling GinInitBuffer
*/
Buffer
2006-10-04 02:30:14 +02:00
GinNewBuffer(Relation index)
{
Buffer buffer;
bool needLock;
/* First, try to get a page from FSM */
2006-10-04 02:30:14 +02:00
for (;;)
{
BlockNumber blkno = GetFreeIndexPage(index);
2006-10-04 02:30:14 +02:00
if (blkno == InvalidBlockNumber)
break;
buffer = ReadBuffer(index, blkno);
/*
* We have to guard against the possibility that someone else already
* recycled this page; the buffer may be locked if so.
*/
2006-10-04 02:30:14 +02:00
if (ConditionalLockBuffer(buffer))
{
Page page = BufferGetPage(buffer);
if (PageIsNew(page))
2006-10-04 02:30:14 +02:00
return buffer; /* OK to use, if never initialized */
if (GinPageIsDeleted(page))
2006-10-04 02:30:14 +02:00
return buffer; /* OK to use */
LockBuffer(buffer, GIN_UNLOCK);
}
/* Can't use it, so release buffer and try again */
ReleaseBuffer(buffer);
}
/* Must extend the file */
needLock = !RELATION_IS_LOCAL(index);
if (needLock)
LockRelationForExtension(index, ExclusiveLock);
buffer = ReadBuffer(index, P_NEW);
LockBuffer(buffer, GIN_EXCLUSIVE);
if (needLock)
UnlockRelationForExtension(index, ExclusiveLock);
return buffer;
}
void
2006-10-04 02:30:14 +02:00
GinInitPage(Page page, uint32 f, Size pageSize)
{
GinPageOpaque opaque;
PageInit(page, pageSize, sizeof(GinPageOpaqueData));
opaque = GinPageGetOpaque(page);
2006-10-04 02:30:14 +02:00
memset(opaque, 0, sizeof(GinPageOpaqueData));
opaque->flags = f;
opaque->rightlink = InvalidBlockNumber;
}
void
2006-10-04 02:30:14 +02:00
GinInitBuffer(Buffer b, uint32 f)
{
GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
}
void
GinInitMetabuffer(Buffer b)
{
GinMetaPageData *metadata;
Page page = BufferGetPage(b);
GinInitPage(page, GIN_META, BufferGetPageSize(b));
metadata = GinPageGetMeta(page);
metadata->head = metadata->tail = InvalidBlockNumber;
metadata->tailFreeSize = 0;
metadata->nPendingPages = 0;
metadata->nPendingHeapTuples = 0;
metadata->nTotalPages = 0;
metadata->nEntryPages = 0;
metadata->nDataPages = 0;
metadata->nEntries = 0;
}
int
ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
2006-10-04 02:30:14 +02:00
{
return DatumGetInt32(FunctionCall2(&ginstate->compareFn[attnum - 1],
a, b));
}
int
ginCompareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
OffsetNumber attnum_b, Datum b)
{
if (attnum_a == attnum_b)
return ginCompareEntries(ginstate, attnum_a, a, b);
return (attnum_a < attnum_b) ? -1 : 1;
}
typedef struct
{
FmgrInfo *cmpDatumFunc;
bool *needUnique;
} cmpEntriesData;
static int
cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
2006-10-04 02:30:14 +02:00
{
int res = DatumGetInt32(FunctionCall2(arg->cmpDatumFunc,
*a, *b));
2006-10-04 02:30:14 +02:00
if (res == 0)
*(arg->needUnique) = TRUE;
return res;
}
2006-10-04 02:30:14 +02:00
Datum *
ginExtractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries,
bool *needUnique)
2006-10-04 02:30:14 +02:00
{
Datum *entries;
entries = (Datum *) DatumGetPointer(FunctionCall2(
&ginstate->extractValueFn[attnum - 1],
2006-10-04 02:30:14 +02:00
value,
PointerGetDatum(nentries)
));
2006-10-04 02:30:14 +02:00
if (entries == NULL)
*nentries = 0;
*needUnique = FALSE;
2006-10-04 02:30:14 +02:00
if (*nentries > 1)
{
cmpEntriesData arg;
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.needUnique = needUnique;
qsort_arg(entries, *nentries, sizeof(Datum),
(qsort_arg_comparator) cmpEntries, (void *) &arg);
}
return entries;
}
2006-10-04 02:30:14 +02:00
Datum *
ginExtractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries)
2006-10-04 02:30:14 +02:00
{
bool needUnique;
Datum *entries = ginExtractEntriesS(ginstate, attnum, value, nentries,
&needUnique);
if (needUnique)
2006-10-04 02:30:14 +02:00
{
Datum *ptr,
*res;
ptr = res = entries;
2006-10-04 02:30:14 +02:00
while (ptr - entries < *nentries)
{
if (ginCompareEntries(ginstate, attnum, *ptr, *res) != 0)
*(++res) = *ptr++;
else
ptr++;
}
*nentries = res + 1 - entries;
}
return entries;
}
Datum
ginoptions(PG_FUNCTION_ARGS)
{
Datum reloptions = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1);
relopt_value *options;
GinOptions *rdopts;
int numoptions;
static const relopt_parse_elt tab[] = {
{"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}
};
options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
&numoptions);
/* if none set, we're done */
if (numoptions == 0)
PG_RETURN_NULL();
rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
validate, tab, lengthof(tab));
pfree(options);
PG_RETURN_BYTEA_P(rdopts);
}
/*
* Fetch index's statistical data into *stats
*
* Note: in the result, nPendingPages can be trusted to be up-to-date,
* but the other fields are as of the last VACUUM.
*/
void
ginGetStats(Relation index, GinStatsData *stats)
{
Buffer metabuffer;
Page metapage;
GinMetaPageData *metadata;
metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
LockBuffer(metabuffer, GIN_SHARE);
metapage = BufferGetPage(metabuffer);
metadata = GinPageGetMeta(metapage);
stats->nPendingPages = metadata->nPendingPages;
stats->nTotalPages = metadata->nTotalPages;
stats->nEntryPages = metadata->nEntryPages;
stats->nDataPages = metadata->nDataPages;
stats->nEntries = metadata->nEntries;
UnlockReleaseBuffer(metabuffer);
}
/*
* Write the given statistics to the index's metapage
*
* Note: nPendingPages is *not* copied over
*/
void
ginUpdateStats(Relation index, const GinStatsData *stats)
{
Buffer metabuffer;
Page metapage;
GinMetaPageData *metadata;
metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
LockBuffer(metabuffer, GIN_EXCLUSIVE);
metapage = BufferGetPage(metabuffer);
metadata = GinPageGetMeta(metapage);
START_CRIT_SECTION();
metadata->nTotalPages = stats->nTotalPages;
metadata->nEntryPages = stats->nEntryPages;
metadata->nDataPages = stats->nDataPages;
metadata->nEntries = stats->nEntries;
MarkBufferDirty(metabuffer);
if (RelationNeedsWAL(index))
{
XLogRecPtr recptr;
ginxlogUpdateMeta data;
XLogRecData rdata;
data.node = index->rd_node;
data.ntuples = 0;
data.newRightlink = data.prevTail = InvalidBlockNumber;
memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
rdata.buffer = InvalidBuffer;
rdata.data = (char *) &data;
rdata.len = sizeof(ginxlogUpdateMeta);
rdata.next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, &rdata);
PageSetLSN(metapage, recptr);
PageSetTLI(metapage, ThisTimeLineID);
}
UnlockReleaseBuffer(metabuffer);
END_CRIT_SECTION();
}