2006-05-02 13:28:56 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* ginutil.c
|
2006-10-04 02:30:14 +02:00
|
|
|
* utilities routines for the postgres inverted index access method.
|
2006-05-02 13:28:56 +02:00
|
|
|
*
|
|
|
|
*
|
2010-01-02 17:58:17 +01:00
|
|
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
2006-05-02 13:28:56 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-01-02 17:58:17 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.23 2010/01/02 16:57:33 momjian Exp $
|
2006-05-02 13:28:56 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/gin.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "access/reloptions.h"
|
2009-06-11 16:49:15 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "storage/bufmgr.h"
|
2006-05-02 13:28:56 +02:00
|
|
|
#include "storage/freespace.h"
|
2008-09-30 12:52:14 +02:00
|
|
|
#include "storage/indexfsm.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "storage/lmgr.h"
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
void
|
|
|
|
initGinState(GinState *state, Relation index)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
int i;
|
2008-07-11 23:06:29 +02:00
|
|
|
|
|
|
|
state->origTupdesc = index->rd_att;
|
|
|
|
|
|
|
|
state->oneCol = (index->rd_att->natts == 1) ? true : false;
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
for (i = 0; i < index->rd_att->natts; i++)
|
2008-07-11 23:06:29 +02:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
state->tupdesc[i] = CreateTemplateTupleDesc(2, false);
|
2008-07-11 23:06:29 +02:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
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
|
|
|
|
);
|
2008-07-11 23:06:29 +02:00
|
|
|
|
|
|
|
fmgr_info_copy(&(state->compareFn[i]),
|
2009-06-11 16:49:15 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
|
|
|
|
CurrentMemoryContext);
|
2008-07-11 23:06:29 +02:00
|
|
|
fmgr_info_copy(&(state->extractValueFn[i]),
|
2009-06-11 16:49:15 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
|
|
|
|
CurrentMemoryContext);
|
2008-07-11 23:06:29 +02:00
|
|
|
fmgr_info_copy(&(state->extractQueryFn[i]),
|
2009-06-11 16:49:15 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
|
|
|
|
CurrentMemoryContext);
|
2008-07-11 23:06:29 +02:00
|
|
|
fmgr_info_copy(&(state->consistentFn[i]),
|
2009-06-11 16:49:15 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
|
|
|
|
CurrentMemoryContext);
|
2008-07-11 23:06:29 +02:00
|
|
|
|
|
|
|
/*
|
2009-03-24 21:17:18 +01:00
|
|
|
* Check opclass capability to do partial match.
|
2008-07-11 23:06:29 +02:00
|
|
|
*/
|
2009-06-11 16:49:15 +02:00
|
|
|
if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
|
2008-07-11 23:06:29 +02:00
|
|
|
{
|
|
|
|
fmgr_info_copy(&(state->comparePartialFn[i]),
|
2009-06-11 16:49:15 +02:00
|
|
|
index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
|
2008-07-11 23:06:29 +02:00
|
|
|
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;
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
if (!ginstate->oneCol)
|
2008-05-16 18:31:02 +02:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Datum res;
|
|
|
|
bool isnull;
|
2008-07-11 23:06:29 +02:00
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* First attribute is always int16, so we can safely use any tuple
|
|
|
|
* descriptor to obtain first attribute of tuple
|
2008-07-11 23:06:29 +02:00
|
|
|
*/
|
|
|
|
res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
|
|
|
|
&isnull);
|
|
|
|
Assert(!isnull);
|
2008-05-16 18:31:02 +02:00
|
|
|
|
2008-07-11 23:06:29 +02:00
|
|
|
colN = DatumGetUInt16(res);
|
2009-06-11 16:49:15 +02:00
|
|
|
Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
|
2008-07-11 23:06:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return colN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract stored datum from GIN tuple
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
gin_index_getattr(GinState *ginstate, IndexTuple tuple)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
bool isnull;
|
|
|
|
Datum res;
|
2008-07-11 23:06:29 +02:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
if (ginstate->oneCol)
|
2008-07-11 23:06:29 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Single column index doesn't store attribute numbers in tuples
|
|
|
|
*/
|
|
|
|
res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
|
|
|
|
&isnull);
|
2008-05-16 18:31:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-07-11 23:06:29 +02:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Since the datum type depends on which index column it's from, we
|
|
|
|
* must be careful to use the right tuple descriptor here.
|
2008-07-11 23:06:29 +02:00
|
|
|
*/
|
|
|
|
OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);
|
|
|
|
|
|
|
|
res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
|
|
|
|
ginstate->tupdesc[colN - 1],
|
|
|
|
&isnull);
|
2008-05-16 18:31:02 +02:00
|
|
|
}
|
2008-07-11 23:06:29 +02:00
|
|
|
|
|
|
|
Assert(!isnull);
|
|
|
|
|
|
|
|
return res;
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
/* First, try to get a page from FSM */
|
2006-10-04 02:30:14 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
2008-09-30 12:52:14 +02:00
|
|
|
BlockNumber blkno = GetFreeIndexPage(index);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-05-02 13:28:56 +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);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
if (PageIsNew(page))
|
2006-10-04 02:30:14 +02:00
|
|
|
return buffer; /* OK to use, if never initialized */
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
if (GinPageIsDeleted(page))
|
2006-10-04 02:30:14 +02:00
|
|
|
return buffer; /* OK to use */
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
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;
|
2006-05-02 13:28:56 +02:00
|
|
|
opaque->rightlink = InvalidBlockNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-10-04 02:30:14 +02:00
|
|
|
GinInitBuffer(Buffer b, uint32 f)
|
|
|
|
{
|
|
|
|
GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
2009-03-24 21:17:18 +01:00
|
|
|
void
|
|
|
|
GinInitMetabuffer(Buffer b)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
GinMetaPageData *metadata;
|
|
|
|
Page page = BufferGetPage(b);
|
2009-03-24 21:17:18 +01:00
|
|
|
|
|
|
|
GinInitPage(page, GIN_META, BufferGetPageSize(b));
|
|
|
|
|
|
|
|
metadata = GinPageGetMeta(page);
|
|
|
|
|
|
|
|
metadata->head = metadata->tail = InvalidBlockNumber;
|
|
|
|
metadata->tailFreeSize = 0;
|
|
|
|
metadata->nPendingPages = 0;
|
|
|
|
metadata->nPendingHeapTuples = 0;
|
|
|
|
}
|
|
|
|
|
2006-05-02 13:28:56 +02:00
|
|
|
int
|
2008-07-11 23:06:29 +02:00
|
|
|
compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
return DatumGetInt32(
|
2006-10-04 02:30:14 +02:00
|
|
|
FunctionCall2(
|
2009-06-11 16:49:15 +02:00
|
|
|
&ginstate->compareFn[attnum - 1],
|
2006-10-04 02:30:14 +02:00
|
|
|
a, b
|
|
|
|
)
|
2007-11-15 22:14:46 +01:00
|
|
|
);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
2008-07-11 23:06:29 +02:00
|
|
|
int
|
|
|
|
compareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
|
2009-06-11 16:49:15 +02:00
|
|
|
OffsetNumber attnum_b, Datum b)
|
2008-07-11 23:06:29 +02:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
if (attnum_a == attnum_b)
|
|
|
|
return compareEntries(ginstate, attnum_a, a, b);
|
2008-07-11 23:06:29 +02:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
return (attnum_a < attnum_b) ? -1 : 1;
|
2008-07-11 23:06:29 +02:00
|
|
|
}
|
|
|
|
|
2006-10-05 19:57:40 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
FmgrInfo *cmpDatumFunc;
|
|
|
|
bool *needUnique;
|
2007-11-15 23:25:18 +01:00
|
|
|
} cmpEntriesData;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
static int
|
2007-11-15 23:25:18 +01:00
|
|
|
cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-10-05 19:57:40 +02:00
|
|
|
int res = DatumGetInt32(FunctionCall2(arg->cmpDatumFunc,
|
|
|
|
*a, *b));
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (res == 0)
|
2006-10-05 19:57:40 +02:00
|
|
|
*(arg->needUnique) = TRUE;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Datum *
|
2008-07-11 23:06:29 +02:00
|
|
|
extractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries,
|
2006-10-05 19:57:40 +02:00
|
|
|
bool *needUnique)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
|
|
|
Datum *entries;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-05 19:57:40 +02:00
|
|
|
entries = (Datum *) DatumGetPointer(FunctionCall2(
|
2009-06-11 16:49:15 +02:00
|
|
|
&ginstate->extractValueFn[attnum - 1],
|
2006-10-04 02:30:14 +02:00
|
|
|
value,
|
|
|
|
PointerGetDatum(nentries)
|
2006-10-05 19:57:40 +02:00
|
|
|
));
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (entries == NULL)
|
2006-05-02 13:28:56 +02:00
|
|
|
*nentries = 0;
|
|
|
|
|
2006-10-05 19:57:40 +02:00
|
|
|
*needUnique = FALSE;
|
2006-10-04 02:30:14 +02:00
|
|
|
if (*nentries > 1)
|
|
|
|
{
|
2006-10-05 19:57:40 +02:00
|
|
|
cmpEntriesData arg;
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
|
2006-10-05 19:57:40 +02:00
|
|
|
arg.needUnique = needUnique;
|
|
|
|
qsort_arg(entries, *nentries, sizeof(Datum),
|
|
|
|
(qsort_arg_comparator) cmpEntries, (void *) &arg);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Datum *
|
2008-07-11 23:06:29 +02:00
|
|
|
extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-10-05 19:57:40 +02:00
|
|
|
bool needUnique;
|
2008-07-11 23:06:29 +02:00
|
|
|
Datum *entries = extractEntriesS(ginstate, attnum, value, nentries,
|
2006-10-05 19:57:40 +02:00
|
|
|
&needUnique);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-05 19:57:40 +02:00
|
|
|
if (needUnique)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
|
|
|
Datum *ptr,
|
|
|
|
*res;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
ptr = res = entries;
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
while (ptr - entries < *nentries)
|
|
|
|
{
|
2008-07-11 23:06:29 +02:00
|
|
|
if (compareEntries(ginstate, attnum, *ptr, *res) != 0)
|
2006-05-02 13:28:56 +02:00
|
|
|
*(++res) = *ptr++;
|
|
|
|
else
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*nentries = res + 1 - entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
2006-07-02 04:23:23 +02:00
|
|
|
Datum
|
2006-07-04 00:45:41 +02:00
|
|
|
ginoptions(PG_FUNCTION_ARGS)
|
2006-07-02 04:23:23 +02:00
|
|
|
{
|
2006-07-04 00:45:41 +02:00
|
|
|
Datum reloptions = PG_GETARG_DATUM(0);
|
|
|
|
bool validate = PG_GETARG_BOOL(1);
|
2009-03-24 21:17:18 +01:00
|
|
|
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,
|
2009-06-11 16:49:15 +02:00
|
|
|
validate, tab, lengthof(tab));
|
2009-03-24 21:17:18 +01:00
|
|
|
|
|
|
|
pfree(options);
|
2006-07-04 00:45:41 +02:00
|
|
|
|
2009-03-24 21:17:18 +01:00
|
|
|
PG_RETURN_BYTEA_P(rdopts);
|
2006-07-02 04:23:23 +02:00
|
|
|
}
|