/* * Create a multi-key heap from an array of entries * * entries: the values to convert to a heap. This array will be under mkheap's ownership * alloc_sz: the allocation size of entries: that is, how much room the array has. * cnt: the number of elements in entries which should be used to build the heap * mkctxt: description of the heap to build * * If alloc_sz is zero then entries must be NULL */ MKHeap * mkheap_from_array(MKEntry *entries, int alloc_sz, int cnt, MKContext *mkctxt) { MKHeap *heap = (MKHeap *) palloc(sizeof(MKHeap)); Assert(mkctxt); Assert(alloc_sz >= cnt); AssertEquivalent(entries != NULL, cnt > 0); AssertEquivalent(!entries, cnt == 0); heap->mkctxt = mkctxt; heap->lvtops = palloc0(mkctxt->total_lv * sizeof(MKEntry)); heap->readers = NULL; heap->nreader = 0; AssertImply(alloc_sz == 0, !entries); Assert(cnt >= 0 && cnt <= alloc_sz); heap->p = entries; heap->alloc_size = alloc_sz; heap->count = cnt; heap->maxentry = cnt; #ifdef USE_ASSERT_CHECKING { int i; for (i = 0; i < cnt; ++i) { Assert(mke_get_lv(entries + i) == 0); Assert(mke_get_reader(entries + i) == 0); } } #endif /* * note: see NOTE ON UNIQUENESS CHECKING at the top of this file for * information about why we don't check uniqueness here */ mk_prepare_array(entries, 0, cnt - 1, 0, mkctxt); mkheap_heapify(heap, true); return heap; }
/* * Create a mkheap from an array of readers, which are assumed to provide their * values in sorted order. * * Invoking this causes each reader to be invoked once to get the first value. */ MKHeap * mkheap_from_reader(MKHeapReader *readers, int nreader, MKContext *ctxt) { int i, j; MKHeap *heap = (MKHeap *) palloc(sizeof(MKHeap)); Assert(readers && nreader > 0); Assert(ctxt); heap->mkctxt = ctxt; heap->lvtops = palloc0(ctxt->total_lv * sizeof(MKEntry)); heap->readers = readers; heap->nreader = nreader; /* Create initial heap. */ heap->p = palloc0(sizeof(MKEntry) * (nreader + 1)); heap->alloc_size = nreader + 1; for (i = 0, j = 0; i < nreader; ++i) { MKEntry *e = heap->p + j; bool fOK = readers[i].reader(readers[i].mkhr_ctxt, e); if (fOK) { mke_set_reader(e, i); ++j; } } heap->count = j; heap->maxentry = j; /* at least one reader had a value so convert the values to the heap */ if (j > 0) { mk_prepare_array(heap->p, 0, j - 1, 0, ctxt); mkheap_heapify(heap, true); } return heap; }
/* * Insert an entry and perhaps return the top element of the heap in *e * * Comparison happens from the specified level to the end of levels, as needed: * Return < 0 if smaller than heap top; *e is unchanged * Return = 0 if eq to heap top ; *e is unchanged (but will have value equal to the heap top) * Return > 0 if successfully inserted; *e is populated with the removed heap top * * If 0 would be returned but the heap is marked as needing uniqueness enforcement, error is generated instead */ static int mkheap_putAndGet_impl(MKHeap *mkheap, MKEntry *e) { int c = 0; int toplv; MKEntry tmp; /* can't put+get from an empty heap */ Assert(mkheap->count > 0); if ( mkheap->mkctxt->enforceUnique && mke_has_duplicates_with_root(mkheap)) { /** * See NOTE ON UNIQUENESS CHECKING in the comment at the top of the file * for information about why we check for duplicates here */ ERROR_UNIQUENESS_VIOLATED(); } if(mke_is_empty(e)) { /* adding an empty (sentinel): just remove from count and fallthrough to where top is removed */ --mkheap->count; } else if (mke_get_run(e) != mke_get_run(mkheap->p)) { /* this code assumes that the new one, with lower run, is LARGER than the top -- so it must be larger run */ Assert(mke_get_run(e) > mke_get_run(mkheap->p)); /* when the runs differ it is because we attempted once with the runs equal. * So if level is zero then: the level was zero AND validly prepared for the previous run -- and there is no need to prep again */ if ( mke_get_lv(e) != 0 ) { /* Not same run, at least prepare lv 0 */ if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, 0); mke_set_lv(e, 0); } /* now fall through and let top be returned, new one is also inserted so no change to count */ } else { /* same run so figure out where it fits in relation to the heap top */ int lv = 0; toplv = mke_get_lv(mkheap->p); mke_set_lv(e, lv); /* populate level until we differ from the top element of the heap */ while (lv < toplv) { if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, lv); c = mkheap_compare(mkheap, e, mkheap->lvtops+lv); if(c!=0) break; mke_set_lv(e, ++lv); } /* smaller than top */ if(c<0) return -1; /* we have not done e->lv == toplv yet since we increment at the end of the previous loop. Do it now. */ Assert(mke_get_lv(e) == lv); if(lv == toplv) { if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, lv); c = mkheap_compare(mkheap, e, mkheap->p); if(c < 0) return -1; } if(c==0) { /* * Equal and at top level. * * This means that e is less-than/equal to all entries except the heap top. */ Assert(mke_get_lv(e) == lv); Assert(lv == mke_get_lv(mkheap->p)); /* * Expand more levels of lvtop in the current top and the new one until we detect a difference. */ while(lv < mkheap->mkctxt->total_lv - 1) { mkheap_save_lvtop(mkheap); ++lv; /* expand top */ if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(mkheap->p, mkheap->mkctxt, lv); /* expand new element */ if ( mkheap->mkctxt->fetchForPrep) tupsort_prepare(e, mkheap->mkctxt, lv); mke_set_lv(mkheap->p, lv); mke_set_lv(e, lv); c = mkheap_compare(mkheap, e, mkheap->p); if(c != 0) break; } if(c<=0) { /* if new one is less than current top then we just return that negative comparison */ /* if new one equals the current top then we could do an insert and immediate removal -- but it * won't matter so we simply return right away, leaving *e untouched */ /* enforce uniqueness first */ if(c == 0 && mkheap->mkctxt->enforceUnique) { ERROR_UNIQUENESS_VIOLATED(); } return c; } } } /* Now, I am bigger than top but not definitely smaller/equal to all other entries * * So we will: * return top as *e * do heap shuffling to restore heap ordering */ tmp = *e; *e = mkheap->p[0]; /* Sift down a hole to bottom of (current or next) run, depends on tmp.run */ mkheap_siftdown(mkheap, 0, &tmp); if(mkheap_need_heapify(mkheap)) mkheap_heapify(mkheap, false); if (mkheap->count > 0) { mkheap_update_lvtops(mkheap); } #ifdef USE_ASSERT_CHECKING if(gp_mk_sort_check) mkheap_verify_heap(mkheap, 0); #endif return 1; }