1/*-------------------------------------------------------------------------
4 * Utility routines for the Postgres inverted index access method.
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/gin/ginutil.c
12 *-------------------------------------------------------------------------
34 * GIN handler function: return IndexAmRoutine with access method parameters
97 * initGinState: fill in an empty GinState struct to describe the index
99 * Note: assorted subsidiary data is allocated in the CurrentMemoryContext.
111 state->origTupdesc = origTupdesc;
113 for (
i = 0;
i < origTupdesc->
natts;
i++)
134 * If the compare proc isn't specified in the opclass definition, look
135 * up the index key type's default btree comparator.
151 (
errcode(ERRCODE_UNDEFINED_FUNCTION),
152 errmsg(
"could not identify a comparison function for type %s",
159 /* Opclass must always provide extract procs */
168 * Check opclass capability to do tri-state or binary logic consistent
188 elog(
ERROR,
"missing GIN support function (%d or %d) for attribute %d of index \"%s\"",
194 * Check opclass capability to do partial match.
201 state->canPartialMatch[
i] =
true;
205 state->canPartialMatch[
i] =
false;
209 * If the index column has a specified collation, we should honor that
210 * while doing comparisons. However, we may have a collatable storage
211 * type for a noncollatable indexed data type (for instance, hstore
212 * uses text index entries). If there's no index collation then
213 * specify default collation in case the support functions need
214 * collation. This is harmless if the support functions don't care
215 * about collation, so we just do it unconditionally. (We could
216 * alternatively call get_typcollation, but that seems like expensive
217 * overkill --- there aren't going to be any cases where a GIN storage
218 * type has a nondefault collation.)
223 state->supportCollation[
i] = DEFAULT_COLLATION_OID;
228 * Extract attribute (column) number of stored entry from GIN tuple
237 /* column number is not stored explicitly */
246 * First attribute is always int16, so we can safely use any tuple
247 * descriptor to obtain first attribute of tuple
261 * Extract stored datum (and possible null category) from GIN tuple
273 * Single column index doesn't store attribute numbers in tuples
281 * Since the datum type depends on which index column it's from, we
282 * must be careful to use the right tuple descriptor here.
300 * Allocate a new page (either by recycling, or by extending the index file)
301 * The returned buffer is already pinned and exclusive-locked
302 * Caller is responsible for initializing the page by calling GinInitBuffer
309 /* First, try to get a page from FSM */
320 * We have to guard against the possibility that someone else already
321 * recycled this page; the buffer may be locked if so.
326 return buffer;
/* OK to use */
331 /* Can't use it, so release buffer and try again */
335 /* Must extend the file */
381 * Set pd_lower just past the end of the metadata. This is essential,
382 * because without doing so, metadata will be lost if xlog.c compresses
390 * Compare two keys of the same index column
397 /* if not of same null category, sort by that first */
398 if (categorya != categoryb)
399 return (categorya < categoryb) ? -1 : 1;
401 /* all null items in same category are equal */
405 /* both not null, so safe to call the compareFn */
412 * Compare two keys of possibly different index columns
419 /* attribute number is the first sort key */
420 if (attnuma != attnumb)
421 return (attnuma < attnumb) ? -1 : 1;
428 * Support for sorting key datums in ginExtractEntries
430 * Note: we only have to worry about null and not-null keys here;
431 * ginExtractEntries never generates more than one placeholder null,
432 * so it doesn't have to sort those.
458 res = 0;
/* NULL "=" NULL */
460 res = 1;
/* NULL ">" not-NULL */
463 res = -1;
/* not-NULL "<" NULL */
470 * Detect if we have any duplicates. If there are equal keys, qsort must
471 * compare them at some point, else it wouldn't know whether one should go
472 * before or after the other.
475 data->haveDups =
true;
482 * Extract the index key values from an indexable item
484 * The resulting key values are sorted, and any duplicates are removed.
485 * This avoids generating redundant index entries.
497 * We don't call the extractValueFn on a null item. Instead generate a
504 entries[0] = (
Datum) 0;
510 /* OK, call the opclass's extractValueFn */
511 nullFlags = NULL;
/* in case extractValue doesn't set it */
520 * Generate a placeholder if the item contained no keys.
522 if (entries == NULL || *nentries <= 0)
526 entries[0] = (
Datum) 0;
533 * If the extractValueFn didn't create a nullFlags array, create one,
534 * assuming that everything's non-null.
536 if (nullFlags == NULL)
537 nullFlags = (
bool *)
palloc0(*nentries *
sizeof(
bool));
540 * If there's more than one key, sort and unique-ify.
542 * XXX Using qsort here is notationally painful, and the overhead is
543 * pretty bad too. For small numbers of keys it'd likely be better to use
544 * a simple insertion sort.
552 for (
i = 0;
i < *nentries;
i++)
560 arg.haveDups =
false;
566 /* there are duplicates, must get rid of 'em */
569 entries[0] = keydata[0].
datum;
570 nullFlags[0] = keydata[0].
isnull;
572 for (
i = 1;
i < *nentries;
i++)
585 /* easy, no duplicates */
586 for (
i = 0;
i < *nentries;
i++)
597 * Create GinNullCategory representation from nullFlags.
600 for (
i = 0;
i < *nentries;
i++)
612 pendingListCleanupSize)}
622 * Fetch index's statistical data into *stats
624 * Note: in the result, nPendingPages can be trusted to be up-to-date,
625 * as can ginVersion; but the other fields are as of the last VACUUM.
650 * Write the given statistics to the index's metapage
652 * Note: nPendingPages and ginVersion are *not* copied over
674 * Set pd_lower just past the end of the metadata. This is essential,
675 * because without doing so, metadata will be lost if xlog.c compresses
676 * the page. (We must do this here because pre-v11 versions of PG did not
677 * set the metapage's pd_lower correctly, so a pg_upgraded index might
678 * contain the wrong value.)
709 * ginbuildphasename() -- Return name of index build phase.
717 return "initializing";
719 return "scanning table";
721 return "sorting tuples (workers)";
723 return "merging tuples (workers)";
725 return "sorting tuples";
727 return "merging tuples";
static bool validate(Port *port, const char *auth)
#define InvalidBlockNumber
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
bool ConditionalLockBuffer(Buffer buffer)
void ReleaseBuffer(Buffer buffer)
void UnlockReleaseBuffer(Buffer buffer)
void MarkBufferDirty(Buffer buffer)
void LockBuffer(Buffer buffer, int mode)
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
static Page BufferGetPage(Buffer buffer)
static Size BufferGetPageSize(Buffer buffer)
void PageInit(Page page, Size pageSize, Size specialSize)
PageHeaderData * PageHeader
static void PageSetLSN(Page page, XLogRecPtr lsn)
#define MemSet(start, val, len)
#define OidIsValid(objectId)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
#define PG_RETURN_POINTER(x)
#define GIN_CONSISTENT_PROC
#define PROGRESS_GIN_PHASE_PERFORMSORT_2
#define PROGRESS_GIN_PHASE_MERGE_1
#define PROGRESS_GIN_PHASE_PERFORMSORT_1
#define GIN_EXTRACTQUERY_PROC
#define GIN_EXTRACTVALUE_PROC
#define PROGRESS_GIN_PHASE_MERGE_2
#define GIN_TRICONSISTENT_PROC
#define GIN_COMPARE_PARTIAL_PROC
#define PROGRESS_GIN_PHASE_INDEXBUILD_TABLESCAN
#define GIN_CURRENT_VERSION
#define GIN_METAPAGE_BLKNO
#define GinGetNullCategory(itup, ginstate)
#define GinPageGetOpaque(page)
#define GIN_CAT_EMPTY_ITEM
signed char GinNullCategory
#define GIN_CAT_NULL_ITEM
#define GinPageGetMeta(p)
int64 gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
IndexBuildResult * ginbuild(Relation heap, Relation index, IndexInfo *indexInfo)
void ginbuildempty(Relation index)
bool gininsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
IndexScanDesc ginbeginscan(Relation rel, int nkeys, int norderbys)
void ginendscan(IndexScanDesc scan)
void ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
void GinInitPage(Page page, uint32 f, Size pageSize)
bytea * ginoptions(Datum reloptions, bool validate)
Datum ginhandler(PG_FUNCTION_ARGS)
void ginGetStats(Relation index, GinStatsData *stats)
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Buffer GinNewBuffer(Relation index)
void GinInitBuffer(Buffer b, uint32 f)
Datum * ginExtractEntries(GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories)
int ginCompareAttEntries(GinState *ginstate, OffsetNumber attnuma, Datum a, GinNullCategory categorya, OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
void GinInitMetabuffer(Buffer b)
static int cmpEntries(const void *a, const void *b, void *arg)
char * ginbuildphasename(int64 phasenum)
void initGinState(GinState *state, Relation index)
int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb)
void ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
bool GinPageIsRecyclable(Page page)
IndexBulkDeleteResult * ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
IndexBulkDeleteResult * ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
bool ginvalidate(Oid opclassoid)
void ginadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
#define XLOG_GIN_UPDATE_META_PAGE
Assert(PointerIsAligned(start, uint64))
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
BlockNumber GetFreeIndexPage(Relation rel)
static Datum index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
void pfree(void *pointer)
void * palloc0(Size size)
MemoryContext CurrentMemoryContext
#define START_CRIT_SECTION()
#define END_CRIT_SECTION()
#define OffsetNumberNext(offsetNumber)
#define FirstOffsetNumber
FormData_pg_attribute * Form_pg_attribute
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
static Datum PointerGetDatum(const void *X)
static uint16 DatumGetUInt16(Datum X)
static Pointer DatumGetPointer(Datum X)
static int32 DatumGetInt32(Datum X)
#define PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE
#define RelationGetDescr(relation)
#define RelationGetRelationName(relation)
#define RelationNeedsWAL(relation)
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
void gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
BlockNumber nPendingPages
TupleDesc tupdesc[INDEX_MAX_KEYS]
FmgrInfo extractValueFn[INDEX_MAX_KEYS]
Oid supportCollation[INDEX_MAX_KEYS]
FmgrInfo compareFn[INDEX_MAX_KEYS]
BlockNumber nPendingPages
ambuildphasename_function ambuildphasename
ambuildempty_function ambuildempty
amvacuumcleanup_function amvacuumcleanup
amoptions_function amoptions
amestimateparallelscan_function amestimateparallelscan
amrestrpos_function amrestrpos
aminsert_function aminsert
amendscan_function amendscan
amparallelrescan_function amparallelrescan
bool amconsistentordering
amcostestimate_function amcostestimate
amadjustmembers_function amadjustmembers
amgettuple_function amgettuple
amcanreturn_function amcanreturn
amgetbitmap_function amgetbitmap
amproperty_function amproperty
ambulkdelete_function ambulkdelete
amvalidate_function amvalidate
ammarkpos_function ammarkpos
bool amusemaintenanceworkmem
ambeginscan_function ambeginscan
amrescan_function amrescan
aminitparallelscan_function aminitparallelscan
uint8 amparallelvacuumoptions
aminsertcleanup_function aminsertcleanup
amgettreeheight_function amgettreeheight
bool amconsistentequality
TupleDesc CreateTemplateTupleDesc(int natts)
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
#define TYPECACHE_CMP_PROC_FINFO
#define VACUUM_OPTION_PARALLEL_CLEANUP
#define VACUUM_OPTION_PARALLEL_BULKDEL
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
void XLogRegisterData(const void *data, uint32 len)
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
void XLogBeginInsert(void)