mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
discussion (including making def_arg allow reserved words), add missed opt_definition for UNIQUE case. Put the reloptions support code in a less random place (I chose to make a new file access/common/reloptions.c). Eliminate header inclusion creep. Make the index options functions safely user-callable (seems like client apps might like to be able to test validity of options before trying to make an index). Reduce overhead for normal case with no options by allowing rd_options to be NULL. Fix some unmaintainably klugy code, including getting rid of Natts_pg_class_fixed at long last. Some stylistic cleanup too, and pay attention to keeping comments in sync with code. Documentation still needs work, though I did fix the omissions in catalogs.sgml and indexam.sgml.
226 lines
5.0 KiB
C
226 lines
5.0 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* ginutil.c
|
|
* utilities routines for the postgres inverted index access method.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.3 2006/07/03 22:45:36 tgl Exp $
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "access/genam.h"
|
|
#include "access/gin.h"
|
|
#include "access/heapam.h"
|
|
#include "access/reloptions.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/freespace.h"
|
|
|
|
void
|
|
initGinState( GinState *state, Relation index ) {
|
|
if ( index->rd_att->natts != 1 )
|
|
elog(ERROR, "numberOfAttributes %d != 1",
|
|
index->rd_att->natts);
|
|
|
|
state->tupdesc = index->rd_att;
|
|
|
|
fmgr_info_copy(&(state->compareFn),
|
|
index_getprocinfo(index, 1, GIN_COMPARE_PROC),
|
|
CurrentMemoryContext);
|
|
fmgr_info_copy(&(state->extractValueFn),
|
|
index_getprocinfo(index, 1, GIN_EXTRACTVALUE_PROC),
|
|
CurrentMemoryContext);
|
|
fmgr_info_copy(&(state->extractQueryFn),
|
|
index_getprocinfo(index, 1, GIN_EXTRACTQUERY_PROC),
|
|
CurrentMemoryContext);
|
|
fmgr_info_copy(&(state->consistentFn),
|
|
index_getprocinfo(index, 1, GIN_CONSISTENT_PROC),
|
|
CurrentMemoryContext);
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
GinNewBuffer(Relation index) {
|
|
Buffer buffer;
|
|
bool needLock;
|
|
|
|
/* First, try to get a page from FSM */
|
|
for(;;) {
|
|
BlockNumber blkno = GetFreeIndexPage(&index->rd_node);
|
|
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.
|
|
*/
|
|
if (ConditionalLockBuffer(buffer)) {
|
|
Page page = BufferGetPage(buffer);
|
|
|
|
if (PageIsNew(page))
|
|
return buffer; /* OK to use, if never initialized */
|
|
|
|
if (GinPageIsDeleted(page))
|
|
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
|
|
GinInitPage(Page page, uint32 f, Size pageSize) {
|
|
GinPageOpaque opaque;
|
|
|
|
PageInit(page, pageSize, sizeof(GinPageOpaqueData));
|
|
|
|
opaque = GinPageGetOpaque(page);
|
|
memset( opaque, 0, sizeof(GinPageOpaqueData) );
|
|
opaque->flags = f;
|
|
opaque->rightlink = InvalidBlockNumber;
|
|
}
|
|
|
|
void
|
|
GinInitBuffer(Buffer b, uint32 f) {
|
|
GinInitPage( BufferGetPage(b), f, BufferGetPageSize(b) );
|
|
}
|
|
|
|
int
|
|
compareEntries(GinState *ginstate, Datum a, Datum b) {
|
|
return DatumGetInt32(
|
|
FunctionCall2(
|
|
&ginstate->compareFn,
|
|
a, b
|
|
)
|
|
);
|
|
}
|
|
|
|
static FmgrInfo* cmpDatumPtr=NULL;
|
|
static bool needUnique = FALSE;
|
|
|
|
static int
|
|
cmpEntries(const void * a, const void * b) {
|
|
int res = DatumGetInt32(
|
|
FunctionCall2(
|
|
cmpDatumPtr,
|
|
*(Datum*)a,
|
|
*(Datum*)b
|
|
)
|
|
);
|
|
|
|
if ( res == 0 )
|
|
needUnique = TRUE;
|
|
|
|
return res;
|
|
}
|
|
|
|
Datum*
|
|
extractEntriesS(GinState *ginstate, Datum value, uint32 *nentries) {
|
|
Datum *entries;
|
|
|
|
entries = (Datum*)DatumGetPointer(
|
|
FunctionCall2(
|
|
&ginstate->extractValueFn,
|
|
value,
|
|
PointerGetDatum( nentries )
|
|
)
|
|
);
|
|
|
|
if ( entries == NULL )
|
|
*nentries = 0;
|
|
|
|
if ( *nentries > 1 ) {
|
|
cmpDatumPtr = &ginstate->compareFn;
|
|
needUnique = FALSE;
|
|
qsort(entries, *nentries, sizeof(Datum), cmpEntries);
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
|
|
|
|
Datum*
|
|
extractEntriesSU(GinState *ginstate, Datum value, uint32 *nentries) {
|
|
Datum *entries = extractEntriesS(ginstate, value, nentries);
|
|
|
|
if ( *nentries>1 && needUnique ) {
|
|
Datum *ptr, *res;
|
|
|
|
ptr = res = entries;
|
|
|
|
while( ptr - entries < *nentries ) {
|
|
if ( compareEntries(ginstate, *ptr, *res ) != 0 )
|
|
*(++res) = *ptr++;
|
|
else
|
|
ptr++;
|
|
}
|
|
|
|
*nentries = res + 1 - entries;
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
|
|
/*
|
|
* It's analog of PageGetTempPage(), but copies whole page
|
|
*/
|
|
Page
|
|
GinPageGetCopyPage( Page page ) {
|
|
Size pageSize = PageGetPageSize( page );
|
|
Page tmppage;
|
|
|
|
tmppage=(Page)palloc( pageSize );
|
|
memcpy( tmppage, page, pageSize );
|
|
|
|
return tmppage;
|
|
}
|
|
|
|
Datum
|
|
ginoptions(PG_FUNCTION_ARGS)
|
|
{
|
|
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...)
|
|
*/
|
|
#define GIN_MIN_FILLFACTOR 50
|
|
#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();
|
|
}
|