#include "postgres.h"
#include "access/gin_private.h"
#include "access/reloptions.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
Go to the source code of this file.
static int cmpEntries | ( | const void * | a, | |
const void * | b, | |||
void * | arg | |||
) | [static] |
Definition at line 335 of file ginutil.c.
References cmpEntriesArg::cmpDatumFunc, cmpEntriesArg::collation, keyEntryData::datum, DatumGetInt32, FunctionCall2Coll(), cmpEntriesArg::haveDups, and keyEntryData::isnull.
Referenced by ginExtractEntries().
{ const keyEntryData *aa = (const keyEntryData *) a; const keyEntryData *bb = (const keyEntryData *) b; cmpEntriesArg *data = (cmpEntriesArg *) arg; int res; if (aa->isnull) { if (bb->isnull) res = 0; /* NULL "=" NULL */ else res = 1; /* NULL ">" not-NULL */ } else if (bb->isnull) res = -1; /* not-NULL "<" NULL */ else res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc, data->collation, aa->datum, bb->datum)); /* * Detect if we have any duplicates. If there are equal keys, qsort must * compare them at some point, else it wouldn't know whether one should go * before or after the other. */ if (res == 0) data->haveDups = true; return res; }
int ginCompareAttEntries | ( | GinState * | ginstate, | |
OffsetNumber | attnuma, | |||
Datum | a, | |||
GinNullCategory | categorya, | |||
OffsetNumber | attnumb, | |||
Datum | b, | |||
GinNullCategory | categoryb | |||
) |
Definition at line 302 of file ginutil.c.
References ginCompareEntries().
Referenced by cmpEntryAccumulator(), entryIsMoveRight(), entryLocateEntry(), and entryLocateLeafEntry().
{ /* attribute number is the first sort key */ if (attnuma != attnumb) return (attnuma < attnumb) ? -1 : 1; return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb); }
int ginCompareEntries | ( | GinState * | ginstate, | |
OffsetNumber | attnum, | |||
Datum | a, | |||
GinNullCategory | categorya, | |||
Datum | b, | |||
GinNullCategory | categoryb | |||
) |
Definition at line 280 of file ginutil.c.
References GinState::compareFn, DatumGetInt32, FunctionCall2Coll(), GIN_CAT_NORM_KEY, and GinState::supportCollation.
Referenced by collectMatchBitmap(), collectMatchesForHeapRow(), ginCompareAttEntries(), and ginFillScanEntry().
{ /* if not of same null category, sort by that first */ if (categorya != categoryb) return (categorya < categoryb) ? -1 : 1; /* all null items in same category are equal */ if (categorya != GIN_CAT_NORM_KEY) return 0; /* both not null, so safe to call the compareFn */ return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1], ginstate->supportCollation[attnum - 1], a, b)); }
Datum* ginExtractEntries | ( | GinState * | ginstate, | |
OffsetNumber | attnum, | |||
Datum | value, | |||
bool | isNull, | |||
int32 * | nentries, | |||
GinNullCategory ** | categories | |||
) |
Definition at line 375 of file ginutil.c.
References arg, cmpEntriesArg::cmpDatumFunc, cmpEntries(), cmpEntriesArg::collation, GinState::compareFn, keyEntryData::datum, DatumGetPointer, GinState::extractValueFn, FunctionCall3Coll(), cmpEntriesArg::haveDups, i, keyEntryData::isnull, NULL, palloc(), palloc0(), pfree(), PointerGetDatum, qsort_arg(), and GinState::supportCollation.
Referenced by ginHeapTupleBulkInsert(), ginHeapTupleFastCollect(), and ginHeapTupleInsert().
{ Datum *entries; bool *nullFlags; int32 i; /* * We don't call the extractValueFn on a null item. Instead generate a * placeholder. */ if (isNull) { *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); entries[0] = (Datum) 0; *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory)); (*categories)[0] = GIN_CAT_NULL_ITEM; return entries; } /* OK, call the opclass's extractValueFn */ nullFlags = NULL; /* in case extractValue doesn't set it */ entries = (Datum *) DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1], ginstate->supportCollation[attnum - 1], value, PointerGetDatum(nentries), PointerGetDatum(&nullFlags))); /* * Generate a placeholder if the item contained no keys. */ if (entries == NULL || *nentries <= 0) { *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); entries[0] = (Datum) 0; *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory)); (*categories)[0] = GIN_CAT_EMPTY_ITEM; return entries; } /* * If the extractValueFn didn't create a nullFlags array, create one, * assuming that everything's non-null. Otherwise, run through the array * and make sure each value is exactly 0 or 1; this ensures binary * compatibility with the GinNullCategory representation. */ if (nullFlags == NULL) nullFlags = (bool *) palloc0(*nentries * sizeof(bool)); else { for (i = 0; i < *nentries; i++) nullFlags[i] = (nullFlags[i] ? true : false); } /* now we can use the nullFlags as category codes */ *categories = (GinNullCategory *) nullFlags; /* * If there's more than one key, sort and unique-ify. * * XXX Using qsort here is notationally painful, and the overhead is * pretty bad too. For small numbers of keys it'd likely be better to use * a simple insertion sort. */ if (*nentries > 1) { keyEntryData *keydata; cmpEntriesArg arg; keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData)); for (i = 0; i < *nentries; i++) { keydata[i].datum = entries[i]; keydata[i].isnull = nullFlags[i]; } arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1]; arg.collation = ginstate->supportCollation[attnum - 1]; arg.haveDups = false; qsort_arg(keydata, *nentries, sizeof(keyEntryData), cmpEntries, (void *) &arg); if (arg.haveDups) { /* there are duplicates, must get rid of 'em */ int32 j; entries[0] = keydata[0].datum; nullFlags[0] = keydata[0].isnull; j = 1; for (i = 1; i < *nentries; i++) { if (cmpEntries(&keydata[i - 1], &keydata[i], &arg) != 0) { entries[j] = keydata[i].datum; nullFlags[j] = keydata[i].isnull; j++; } } *nentries = j; } else { /* easy, no duplicates */ for (i = 0; i < *nentries; i++) { entries[i] = keydata[i].datum; nullFlags[i] = keydata[i].isnull; } } pfree(keydata); } return entries; }
void ginGetStats | ( | Relation | index, | |
GinStatsData * | stats | |||
) |
Definition at line 531 of file ginutil.c.
References BufferGetPage, GIN_METAPAGE_BLKNO, GIN_SHARE, GinPageGetMeta, GinMetaPageData::ginVersion, GinStatsData::ginVersion, LockBuffer(), GinMetaPageData::nDataPages, GinStatsData::nDataPages, GinMetaPageData::nEntries, GinStatsData::nEntries, GinMetaPageData::nEntryPages, GinStatsData::nEntryPages, GinMetaPageData::nPendingPages, GinStatsData::nPendingPages, GinMetaPageData::nTotalPages, GinStatsData::nTotalPages, ReadBuffer(), and UnlockReleaseBuffer().
Referenced by gincostestimate(), and ginNewScanKey().
{ 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; stats->ginVersion = metadata->ginVersion; UnlockReleaseBuffer(metabuffer); }
Definition at line 250 of file ginutil.c.
References BufferGetPage, BufferGetPageSize, and GinInitPage().
Referenced by createPostingTree(), ginbuild(), ginbuildempty(), ginInsertValue(), ginRedoCreateIndex(), ginRedoCreatePTree(), ginRedoInsertListPage(), ginRedoSplit(), and writeListPage().
{ GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b)); }
void GinInitMetabuffer | ( | Buffer | b | ) |
Definition at line 256 of file ginutil.c.
References BufferGetPage, BufferGetPageSize, GIN_META, GinInitPage(), GinPageGetMeta, GinMetaPageData::ginVersion, GinMetaPageData::head, GinMetaPageData::nDataPages, GinMetaPageData::nEntries, GinMetaPageData::nEntryPages, GinMetaPageData::nPendingHeapTuples, GinMetaPageData::nPendingPages, GinMetaPageData::nTotalPages, GinMetaPageData::tail, and GinMetaPageData::tailFreeSize.
Referenced by ginbuild(), ginbuildempty(), and ginRedoCreateIndex().
{ 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; metadata->ginVersion = GIN_CURRENT_VERSION; }
Definition at line 237 of file ginutil.c.
References GinPageOpaqueData::flags, GinPageGetOpaque, PageInit(), and GinPageOpaqueData::rightlink.
Referenced by dataSplitPage(), entrySplitPage(), GinInitBuffer(), and GinInitMetabuffer().
{ GinPageOpaque opaque; PageInit(page, pageSize, sizeof(GinPageOpaqueData)); opaque = GinPageGetOpaque(page); memset(opaque, 0, sizeof(GinPageOpaqueData)); opaque->flags = f; opaque->rightlink = InvalidBlockNumber; }
Definition at line 186 of file ginutil.c.
References BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), GIN_EXCLUSIVE, GIN_UNLOCK, GinPageIsDeleted, InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, ReleaseBuffer(), and UnlockRelationForExtension().
Referenced by createPostingTree(), ginbuild(), ginInsertValue(), and makeSublist().
{ Buffer buffer; bool needLock; /* First, try to get a page from FSM */ for (;;) { BlockNumber blkno = GetFreeIndexPage(index); 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; }
Datum ginoptions | ( | PG_FUNCTION_ARGS | ) |
Definition at line 496 of file ginutil.c.
References allocateReloptStruct(), fillRelOptions(), lengthof, offsetof, parseRelOptions(), pfree(), PG_GETARG_BOOL, PG_GETARG_DATUM, PG_RETURN_BYTEA_P, PG_RETURN_NULL, and RELOPT_KIND_GIN.
{ 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); }
OffsetNumber gintuple_get_attrnum | ( | GinState * | ginstate, | |
IndexTuple | tuple | |||
) |
Definition at line 112 of file ginutil.c.
References Assert, DatumGetUInt16, FirstOffsetNumber, index_getattr, GinState::oneCol, and GinState::tupdesc.
Referenced by addItemPointersToLeafTuple(), collectMatchBitmap(), collectMatchesForHeapRow(), entryIsMoveRight(), entryLocateEntry(), entryLocateLeafEntry(), gintuple_get_key(), ginVacuumEntryPage(), matchPartialInPendingList(), and processPendingPage().
{ OffsetNumber colN; if (ginstate->oneCol) { /* column number is not stored explicitly */ colN = FirstOffsetNumber; } else { 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; }
Datum gintuple_get_key | ( | GinState * | ginstate, | |
IndexTuple | tuple, | |||
GinNullCategory * | category | |||
) |
Definition at line 145 of file ginutil.c.
References FirstOffsetNumber, GinGetNullCategory, gintuple_get_attrnum(), index_getattr, OffsetNumberNext, GinState::oneCol, GinState::origTupdesc, and GinState::tupdesc.
Referenced by addItemPointersToLeafTuple(), collectMatchBitmap(), collectMatchesForHeapRow(), entryIsMoveRight(), entryLocateEntry(), entryLocateLeafEntry(), ginVacuumEntryPage(), matchPartialInPendingList(), and processPendingPage().
{ Datum res; bool isnull; 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); } if (isnull) *category = GinGetNullCategory(tuple, ginstate); else *category = GIN_CAT_NORM_KEY; return res; }
void ginUpdateStats | ( | Relation | index, | |
const GinStatsData * | stats | |||
) |
Definition at line 558 of file ginutil.c.
References XLogRecData::buffer, BufferGetPage, XLogRecData::data, END_CRIT_SECTION, GIN_EXCLUSIVE, GIN_METAPAGE_BLKNO, GinPageGetMeta, XLogRecData::len, LockBuffer(), MarkBufferDirty(), ginxlogUpdateMeta::metadata, GinStatsData::nDataPages, GinMetaPageData::nDataPages, GinStatsData::nEntries, GinMetaPageData::nEntries, GinStatsData::nEntryPages, GinMetaPageData::nEntryPages, ginxlogUpdateMeta::newRightlink, XLogRecData::next, ginxlogUpdateMeta::node, GinStatsData::nTotalPages, GinMetaPageData::nTotalPages, ginxlogUpdateMeta::ntuples, PageSetLSN, ginxlogUpdateMeta::prevTail, RelationData::rd_node, ReadBuffer(), RelationNeedsWAL, START_CRIT_SECTION, UnlockReleaseBuffer(), XLOG_GIN_UPDATE_META_PAGE, and XLogInsert().
Referenced by ginbuild(), and ginvacuumcleanup().
{ 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); } UnlockReleaseBuffer(metabuffer); END_CRIT_SECTION(); }
Definition at line 32 of file ginutil.c.
References tupleDesc::attrs, GinState::canPartialMatch, GinState::compareFn, GinState::comparePartialFn, GinState::consistentFn, CreateTemplateTupleDesc(), CurrentMemoryContext, GinState::extractQueryFn, GinState::extractValueFn, fmgr_info_copy(), GIN_COMPARE_PARTIAL_PROC, GIN_COMPARE_PROC, GIN_CONSISTENT_PROC, GIN_EXTRACTQUERY_PROC, GIN_EXTRACTVALUE_PROC, i, GinState::index, index_getprocid(), index_getprocinfo(), INT2OID, InvalidOid, MemSet, tupleDesc::natts, NULL, OidIsValid, GinState::oneCol, GinState::origTupdesc, RelationData::rd_indcollation, RelationGetDescr, GinState::supportCollation, GinState::tupdesc, TupleDescInitEntry(), and TupleDescInitEntryCollation().
Referenced by ginbeginscan(), ginbuild(), ginbulkdelete(), gininsert(), and ginvacuumcleanup().
{ TupleDesc origTupdesc = RelationGetDescr(index); int i; MemSet(state, 0, sizeof(GinState)); state->index = index; state->oneCol = (origTupdesc->natts == 1) ? true : false; state->origTupdesc = origTupdesc; for (i = 0; i < origTupdesc->natts; i++) { if (state->oneCol) state->tupdesc[i] = state->origTupdesc; else { state->tupdesc[i] = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL, origTupdesc->attrs[i]->atttypid, origTupdesc->attrs[i]->atttypmod, origTupdesc->attrs[i]->attndims); TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2, origTupdesc->attrs[i]->attcollation); } 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; } /* * If the index column has a specified collation, we should honor that * while doing comparisons. However, we may have a collatable storage * type for a noncollatable indexed data type (for instance, hstore * uses text index entries). If there's no index collation then * specify default collation in case the support functions need * collation. This is harmless if the support functions don't care * about collation, so we just do it unconditionally. (We could * alternatively call get_typcollation, but that seems like expensive * overkill --- there aren't going to be any cases where a GIN storage * type has a nondefault collation.) */ if (OidIsValid(index->rd_indcollation[i])) state->supportCollation[i] = index->rd_indcollation[i]; else state->supportCollation[i] = DEFAULT_COLLATION_OID; } }