aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/gin/ginbulk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/gin/ginbulk.c')
-rw-r--r--src/backend/access/gin/ginbulk.c318
1 files changed, 121 insertions, 197 deletions
diff --git a/src/backend/access/gin/ginbulk.c b/src/backend/access/gin/ginbulk.c
index 8f00e1305b3..accd6640375 100644
--- a/src/backend/access/gin/ginbulk.c
+++ b/src/backend/access/gin/ginbulk.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.17 2010/01/02 16:57:33 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.18 2010/02/11 14:29:50 teodor Exp $
*-------------------------------------------------------------------------
*/
@@ -22,59 +22,60 @@
#define DEF_NENTRY 2048
#define DEF_NPTR 4
-void
-ginInitBA(BuildAccumulator *accum)
+static void*
+ginAppendData(void *old, void *new, void *arg)
{
- accum->maxdepth = 1;
- accum->stackpos = 0;
- accum->entries = NULL;
- accum->stack = NULL;
- accum->allocatedMemory = 0;
- accum->entryallocator = NULL;
-}
+ EntryAccumulator *eo = (EntryAccumulator*)old,
+ *en = (EntryAccumulator*)new;
-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;
-}
+ BuildAccumulator *accum = (BuildAccumulator*)arg;
-/*
- * 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)
+ if (eo->number >= eo->length)
{
- accum->allocatedMemory -= GetMemoryChunkSpace(entry->list);
- entry->length *= 2;
- entry->list = (ItemPointerData *) repalloc(entry->list,
- sizeof(ItemPointerData) * entry->length);
- accum->allocatedMemory += GetMemoryChunkSpace(entry->list);
+ accum->allocatedMemory -= GetMemoryChunkSpace(eo->list);
+ eo->length *= 2;
+ eo->list = (ItemPointerData *) repalloc(eo->list,
+ sizeof(ItemPointerData) * eo->length);
+ accum->allocatedMemory += GetMemoryChunkSpace(eo->list);
}
- if (entry->shouldSort == FALSE)
+ /* If item pointers are not ordered, they will need to be sorted. */
+ if (eo->shouldSort == FALSE)
{
- int res = compareItemPointers(entry->list + entry->number - 1, heapptr);
+ int res;
+ res = compareItemPointers(eo->list + eo->number - 1, en->list);
Assert(res != 0);
if (res > 0)
- entry->shouldSort = TRUE;
+ eo->shouldSort = TRUE;
}
- entry->list[entry->number] = *heapptr;
- entry->number++;
+ eo->list[eo->number] = en->list[0];
+ eo->number++;
+
+ return old;
+}
+
+static int
+cmpEntryAccumulator(const void *a, const void *b, void *arg)
+{
+ EntryAccumulator *ea = (EntryAccumulator*)a;
+ EntryAccumulator *eb = (EntryAccumulator*)b;
+ BuildAccumulator *accum = (BuildAccumulator*)arg;
+
+ return compareAttEntries(accum->ginstate, ea->attnum, ea->value,
+ eb->attnum, eb->value);
+}
+
+void
+ginInitBA(BuildAccumulator *accum)
+{
+ accum->allocatedMemory = 0;
+ accum->entryallocator = NULL;
+ accum->tree = rb_create(cmpEntryAccumulator, ginAppendData, NULL, accum);
+ accum->iterator = NULL;
+ accum->tmpList = NULL;
}
/*
@@ -103,111 +104,104 @@ getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum 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)
+ EntryAccumulator *key,
+ *ea;
+
+ /*
+ * Allocate memory by rather big chunk to decrease overhead, we don't
+ * keep pointer to previously allocated chunks because they will free
+ * by MemoryContextReset() call.
+ */
+ if (accum->entryallocator == NULL || accum->length >= DEF_NENTRY)
{
- 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++;
+ accum->entryallocator = palloc(sizeof(EntryAccumulator) * DEF_NENTRY);
+ accum->allocatedMemory += GetMemoryChunkSpace(accum->entryallocator);
+ accum->length = 0;
}
- if (depth > accum->maxdepth)
- accum->maxdepth = depth;
+ /* "Allocate" new key in chunk */
+ key = accum->entryallocator + accum->length;
+ accum->length++;
+
+ key->attnum = attnum;
+ key->value = entry;
+ /* To prevent multiple palloc/pfree cycles, we reuse array */
+ if (accum->tmpList == NULL)
+ accum->tmpList =
+ (ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR);
+ key->list = accum->tmpList;
+ key->list[0] = *heapptr;
+
+ ea = rb_insert(accum->tree, key);
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;
- }
+ /*
+ * The key has been inserted, so continue initialization.
+ */
+ key->value = getDatumCopy(accum, attnum, entry);
+ key->length = DEF_NPTR;
+ key->number = 1;
+ key->shouldSort = FALSE;
+ accum->allocatedMemory += GetMemoryChunkSpace(key->list);
+ accum->tmpList = NULL;
}
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);
+ {
+ /*
+ * The key has been appended, so "free" allocated
+ * key by decrementing chunk's counter.
+ */
+ accum->length--;
+ }
}
/*
- * 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.
+ * Insert one heap pointer.
+ *
+ * Since the entries are being inserted into a balanced binary tree, you
+ * might think that the order of insertion wouldn't be critical, but it turns
+ * out that inserting the entries in sorted order results in a lot of
+ * rebalancing operations and is slow. To prevent this, we attempt to insert
+ * the nodes in an order that will produce a nearly-balanced tree if the input
+ * is in fact sorted.
+ *
+ * We do this as follows. First, we imagine that we have an array whose size
+ * is the smallest power of two greater than or equal to the actual array
+ * size. Second, we insert the middle entry of our virtual array into the
+ * tree; then, we insert the middles of each half of out virtual array, then
+ * middles of quarters, etc.
*/
-void
+ void
ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, int32 nentry)
{
- uint32 i,
- nbit = 0,
- offset;
+ uint32 step = nentry;
if (nentry <= 0)
return;
Assert(ItemPointerIsValid(heapptr) && attnum >= FirstOffsetNumber);
- i = nentry - 1;
- for (; i > 0; i >>= 1)
- nbit++;
+ /*
+ * step will contain largest power of 2 and <= nentry
+ */
+ step |= (step >> 1);
+ step |= (step >> 2);
+ step |= (step >> 4);
+ step |= (step >> 8);
+ step |= (step >> 16);
+ step >>= 1;
+ step ++;
- nbit = 1 << nbit;
- offset = (nbit - nentry) / 2;
+ while(step > 0) {
+ int i;
- ginInsertEntry(accum, heapptr, attnum, entries[(nbit >> 1) - offset]);
- ginChooseElem(accum, heapptr, attnum, entries, nentry, 0, nbit, offset);
+ for (i = step - 1; i < nentry && i >= 0; i += step << 1 /* *2 */)
+ ginInsertEntry(accum, heapptr, attnum, entries[i]);
+
+ step >>= 1; /* /2 */
+ }
}
static int
@@ -219,86 +213,16 @@ qsortCompareItemPointers(const void *a, const void *b)
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 (accum->iterator == NULL)
+ accum->iterator = rb_begin_iterate(accum->tree, LeftRightWalk);
+
+ entry = rb_iterate(accum->iterator);
if (entry == NULL)
return NULL;