/*------------------------------------------------------------------------- * * ginbulk.c * routines for fast build of inverted index * * * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.16 2009/06/11 14:48:53 momjian Exp $ *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/gin.h" #include "utils/datum.h" #include "utils/memutils.h" #define DEF_NENTRY 2048 #define DEF_NPTR 4 void ginInitBA(BuildAccumulator *accum) { accum->maxdepth = 1; accum->stackpos = 0; accum->entries = NULL; accum->stack = NULL; accum->allocatedMemory = 0; accum->entryallocator = NULL; } static EntryAccumulator * EAAllocate(BuildAccumulator *accum) { if (accum->entryallocator == NULL || accum->length >= DEF_NENTRY) { accum->entryallocator = palloc(sizeof(EntryAccumulator) * DEF_NENTRY); accum->allocatedMemory += GetMemoryChunkSpace(accum->entryallocator); accum->length = 0; } accum->length++; return accum->entryallocator + accum->length - 1; } /* * Stores heap item pointer. For robust, it checks that * item pointer are ordered */ static void ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heapptr) { if (entry->number >= entry->length) { accum->allocatedMemory -= GetMemoryChunkSpace(entry->list); entry->length *= 2; entry->list = (ItemPointerData *) repalloc(entry->list, sizeof(ItemPointerData) * entry->length); accum->allocatedMemory += GetMemoryChunkSpace(entry->list); } if (entry->shouldSort == FALSE) { int res = compareItemPointers(entry->list + entry->number - 1, heapptr); Assert(res != 0); if (res > 0) entry->shouldSort = TRUE; } entry->list[entry->number] = *heapptr; entry->number++; } /* * This is basically the same as datumCopy(), but modified to count * palloc'd space in accum. */ static Datum getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value) { Form_pg_attribute att = accum->ginstate->origTupdesc->attrs[attnum - 1]; Datum res; if (att->attbyval) res = value; else { res = datumCopy(value, false, att->attlen); accum->allocatedMemory += GetMemoryChunkSpace(DatumGetPointer(res)); } return res; } /* * Find/store one entry from indexed value. */ static void ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum entry) { EntryAccumulator *ea = accum->entries, *pea = NULL; int res = 0; uint32 depth = 1; while (ea) { res = compareAttEntries(accum->ginstate, attnum, entry, ea->attnum, ea->value); if (res == 0) break; /* found */ else { pea = ea; if (res < 0) ea = ea->left; else ea = ea->right; } depth++; } if (depth > accum->maxdepth) accum->maxdepth = depth; if (ea == NULL) { ea = EAAllocate(accum); ea->left = ea->right = NULL; ea->attnum = attnum; ea->value = getDatumCopy(accum, attnum, entry); ea->length = DEF_NPTR; ea->number = 1; ea->shouldSort = FALSE; ea->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR); accum->allocatedMemory += GetMemoryChunkSpace(ea->list); ea->list[0] = *heapptr; if (pea == NULL) accum->entries = ea; else { Assert(res != 0); if (res < 0) pea->left = ea; else pea->right = ea; } } else ginInsertData(accum, ea, heapptr); } /* * insert middle of left part the middle of right one, * then calls itself for each parts */ static void ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, uint32 nentry, uint32 low, uint32 high, uint32 offset) { uint32 pos; uint32 middle = (low + high) >> 1; pos = (low + middle) >> 1; if (low != middle && pos >= offset && pos - offset < nentry) ginInsertEntry(accum, heapptr, attnum, entries[pos - offset]); pos = (high + middle + 1) >> 1; if (middle + 1 != high && pos >= offset && pos - offset < nentry) ginInsertEntry(accum, heapptr, attnum, entries[pos - offset]); if (low != middle) ginChooseElem(accum, heapptr, attnum, entries, nentry, low, middle, offset); if (high != middle + 1) ginChooseElem(accum, heapptr, attnum, entries, nentry, middle + 1, high, offset); } /* * Insert one heap pointer. Suppose entries is sorted. * Insertion order tries to get binary tree balanced: first insert middle value, * next middle on left part and middle of right part. */ void ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, int32 nentry) { uint32 i, nbit = 0, offset; if (nentry <= 0) return; Assert(ItemPointerIsValid(heapptr) && attnum >= FirstOffsetNumber); i = nentry - 1; for (; i > 0; i >>= 1) nbit++; nbit = 1 << nbit; offset = (nbit - nentry) / 2; ginInsertEntry(accum, heapptr, attnum, entries[(nbit >> 1) - offset]); ginChooseElem(accum, heapptr, attnum, entries, nentry, 0, nbit, offset); } static int qsortCompareItemPointers(const void *a, const void *b) { int res = compareItemPointers((ItemPointer) a, (ItemPointer) b); Assert(res != 0); return res; } /* * walk on binary tree and returns ordered nodes */ static EntryAccumulator * walkTree(BuildAccumulator *accum) { EntryAccumulator *entry = accum->stack[accum->stackpos]; if (entry->list != NULL) { /* return entry itself: we already was at left sublink */ return entry; } else if (entry->right && entry->right != accum->stack[accum->stackpos + 1]) { /* go on right sublink */ accum->stackpos++; entry = entry->right; /* find most-left value */ for (;;) { accum->stack[accum->stackpos] = entry; if (entry->left) { accum->stackpos++; entry = entry->left; } else break; } } else { /* we already return all left subtree, itself and right subtree */ if (accum->stackpos == 0) return 0; accum->stackpos--; return walkTree(accum); } return entry; } ItemPointerData * ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *value, uint32 *n) { EntryAccumulator *entry; ItemPointerData *list; if (accum->stack == NULL) { /* first call */ accum->stack = palloc0(sizeof(EntryAccumulator *) * (accum->maxdepth + 1)); accum->allocatedMemory += GetMemoryChunkSpace(accum->stack); entry = accum->entries; if (entry == NULL) return NULL; /* find most-left value */ for (;;) { accum->stack[accum->stackpos] = entry; if (entry->left) { accum->stackpos++; entry = entry->left; } else break; } } else { accum->allocatedMemory -= GetMemoryChunkSpace(accum->stack[accum->stackpos]->list); pfree(accum->stack[accum->stackpos]->list); accum->stack[accum->stackpos]->list = NULL; entry = walkTree(accum); } if (entry == NULL) return NULL; *n = entry->number; *attnum = entry->attnum; *value = entry->value; list = entry->list; Assert(list != NULL); if (entry->shouldSort && entry->number > 1) qsort(list, *n, sizeof(ItemPointerData), qsortCompareItemPointers); return list; }