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
|
|
|
*
|
|
|
|
*
|
|
|
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2006-10-04 02:30:14 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.7 2006/10/04 00:29:48 momjian Exp $
|
2006-05-02 13:28:56 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/gin.h"
|
|
|
|
#include "access/heapam.h"
|
2006-07-04 00:45:41 +02:00
|
|
|
#include "access/reloptions.h"
|
2006-05-02 13:28:56 +02:00
|
|
|
#include "storage/freespace.h"
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
void
|
|
|
|
initGinState(GinState *state, Relation index)
|
|
|
|
{
|
|
|
|
if (index->rd_att->natts != 1)
|
|
|
|
elog(ERROR, "numberOfAttributes %d != 1",
|
|
|
|
index->rd_att->natts);
|
|
|
|
|
2006-05-02 13:28:56 +02:00
|
|
|
state->tupdesc = index->rd_att;
|
|
|
|
|
|
|
|
fmgr_info_copy(&(state->compareFn),
|
2006-10-04 02:30:14 +02:00
|
|
|
index_getprocinfo(index, 1, GIN_COMPARE_PROC),
|
|
|
|
CurrentMemoryContext);
|
2006-05-02 13:28:56 +02:00
|
|
|
fmgr_info_copy(&(state->extractValueFn),
|
2006-10-04 02:30:14 +02:00
|
|
|
index_getprocinfo(index, 1, GIN_EXTRACTVALUE_PROC),
|
|
|
|
CurrentMemoryContext);
|
2006-05-02 13:28:56 +02:00
|
|
|
fmgr_info_copy(&(state->extractQueryFn),
|
2006-10-04 02:30:14 +02:00
|
|
|
index_getprocinfo(index, 1, GIN_EXTRACTQUERY_PROC),
|
|
|
|
CurrentMemoryContext);
|
2006-05-02 13:28:56 +02:00
|
|
|
fmgr_info_copy(&(state->consistentFn),
|
2006-10-04 02:30:14 +02:00
|
|
|
index_getprocinfo(index, 1, GIN_CONSISTENT_PROC),
|
|
|
|
CurrentMemoryContext);
|
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 (;;)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
BlockNumber blkno = GetFreeIndexPage(&index->rd_node);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2006-10-04 02:30:14 +02:00
|
|
|
compareEntries(GinState *ginstate, Datum a, Datum b)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
return DatumGetInt32(
|
2006-10-04 02:30:14 +02:00
|
|
|
FunctionCall2(
|
|
|
|
&ginstate->compareFn,
|
|
|
|
a, b
|
|
|
|
)
|
2006-05-02 13:28:56 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
static FmgrInfo *cmpDatumPtr = NULL;
|
2006-09-05 20:25:10 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
#if defined(__INTEL_COMPILER) && (defined(__ia64__) || defined(__ia64))
|
|
|
|
/*
|
2006-09-05 20:25:10 +02:00
|
|
|
* Intel Compiler on Intel Itanium with -O2 has a bug around
|
|
|
|
* change static variable by user function called from
|
|
|
|
* libc func: it doesn't change. So mark it as volatile.
|
|
|
|
*
|
|
|
|
* It's a pity, but it's impossible to define optimization
|
|
|
|
* level here.
|
|
|
|
*/
|
2006-10-04 02:30:14 +02:00
|
|
|
#define VOLATILE volatile
|
2006-09-05 20:25:10 +02:00
|
|
|
#else
|
|
|
|
#define VOLATILE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static bool VOLATILE needUnique = FALSE;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
static int
|
2006-10-04 02:30:14 +02:00
|
|
|
cmpEntries(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
int res = DatumGetInt32(
|
|
|
|
FunctionCall2(
|
|
|
|
cmpDatumPtr,
|
|
|
|
*(Datum *) a,
|
|
|
|
*(Datum *) b
|
|
|
|
)
|
2006-05-02 13:28:56 +02:00
|
|
|
);
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (res == 0)
|
2006-05-02 13:28:56 +02:00
|
|
|
needUnique = TRUE;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Datum *
|
|
|
|
extractEntriesS(GinState *ginstate, Datum value, uint32 *nentries)
|
|
|
|
{
|
|
|
|
Datum *entries;
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
entries = (Datum *) DatumGetPointer(
|
|
|
|
FunctionCall2(
|
|
|
|
&ginstate->extractValueFn,
|
|
|
|
value,
|
|
|
|
PointerGetDatum(nentries)
|
|
|
|
)
|
|
|
|
);
|
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-04 02:30:14 +02:00
|
|
|
if (*nentries > 1)
|
|
|
|
{
|
2006-05-02 13:28:56 +02:00
|
|
|
cmpDatumPtr = &ginstate->compareFn;
|
|
|
|
needUnique = FALSE;
|
2006-10-04 02:30:14 +02:00
|
|
|
qsort(entries, *nentries, sizeof(Datum), cmpEntries);
|
2006-05-02 13:28:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
Datum *
|
|
|
|
extractEntriesSU(GinState *ginstate, Datum value, uint32 *nentries)
|
|
|
|
{
|
|
|
|
Datum *entries = extractEntriesS(ginstate, value, nentries);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
if (*nentries > 1 && needUnique)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (compareEntries(ginstate, *ptr, *res) != 0)
|
2006-05-02 13:28:56 +02:00
|
|
|
*(++res) = *ptr++;
|
|
|
|
else
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*nentries = res + 1 - entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It's analog of PageGetTempPage(), but copies whole page
|
|
|
|
*/
|
|
|
|
Page
|
2006-10-04 02:30:14 +02:00
|
|
|
GinPageGetCopyPage(Page page)
|
|
|
|
{
|
|
|
|
Size pageSize = PageGetPageSize(page);
|
|
|
|
Page tmppage;
|
|
|
|
|
|
|
|
tmppage = (Page) palloc(pageSize);
|
|
|
|
memcpy(tmppage, page, pageSize);
|
2006-05-02 13:28:56 +02:00
|
|
|
|
|
|
|
return tmppage;
|
|
|
|
}
|
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);
|
|
|
|
bytea *result;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It's not clear that fillfactor is useful for GIN, but for the moment
|
|
|
|
* we'll accept it anyway. (It won't do anything...)
|
|
|
|
*/
|
2006-07-11 23:05:57 +02:00
|
|
|
#define GIN_MIN_FILLFACTOR 10
|
2006-07-04 00:45:41 +02:00
|
|
|
#define GIN_DEFAULT_FILLFACTOR 100
|
|
|
|
|
|
|
|
result = default_reloptions(reloptions, validate,
|
|
|
|
GIN_MIN_FILLFACTOR,
|
|
|
|
GIN_DEFAULT_FILLFACTOR);
|
|
|
|
if (result)
|
|
|
|
PG_RETURN_BYTEA_P(result);
|
|
|
|
PG_RETURN_NULL();
|
2006-07-02 04:23:23 +02:00
|
|
|
}
|