blob: beb9b9bc16847084bb7e7d576854f800a4f7c7c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* tuplesort_mk.h
* Tuplesort Multi Key.
*
*/
#ifndef TUPLESORT_MK_H
#define TUPLESORT_MK_H
/* mk_heap: multi level key heap */
/* mk_qsort: multi level key quick sort */
/* flags, involved in cmp */
#define MKE_CF_NullFirst 1 /* Null is smaller than non null */
#define MKE_CF_NotNull 2
#define MKE_CF_NullLast 3 /* Null is bigger than non null */
#define MKE_CF_NULLBITS 3
#define MKE_CF_Lv 0xFFFC /* Runs use 14 bits */
#define MKE_CF_Run 0x3FFF0000 /* Lv use 14 bits */
#define MKE_CF_Empty 0x40000000 /* Empty use 1 bit */
#define MKE_CF_MAX 0x7FFFFFFF /* max value. */
#define MKE_MAX_LV 0x3FFF
#define MKE_MAX_RUN 0x3FFF
#define MKE_RunShift 16
#define MKE_LvShift 2
/* Top bit is always unused */
/* flags, not comp */
#define MKE_F_RefCnt 1
#define MKE_F_Copied 2
#define MKE_F_Reader 0xFFFC
#define MKE_MAX_READER 0x3FFF
#define MKE_ReaderShift 2
typedef struct MKEntry
{
int32 compflags;
int32 flags;
/**
* The datum for this key
*/
Datum d;
/**
* Ptr to the tuple that contains this entry's key. Is a void * to provide polymorphism: it could be a memtuple, heaptuple, or really anything that has multi-key behavior!
* Deciphering of this field is done by the functions that are passed when the multi-key heap is prepared
*/
void *ptr;
} MKEntry;
/**
* Set flags and compflags to their initial values
*/
static inline void mke_blank(MKEntry *e)
{
e->compflags = (MKE_MAX_LV << MKE_LvShift);
e->flags = 0;
}
static inline void i_clear_flag(int32 *p, int32 flag)
{
*p &= ~flag;
}
static inline bool i_test_flag(int32 *p, int32 flag)
{
return (*p & flag) != 0;
}
static inline void i_set_flag(int32 *p, int32 flag)
{
*p |= flag;
}
static inline int32 i_get_flag(int32 *p, int32 flag, int32 shiftsz)
{
return ((*p & flag) >> shiftsz);
}
/* Getter, Setter */
static inline void mke_set_null(MKEntry *e, bool nullfirst)
{
i_clear_flag(&e->compflags, MKE_CF_NULLBITS);
if (nullfirst)
i_set_flag(&e->compflags, MKE_CF_NullFirst);
else
i_set_flag(&e->compflags, MKE_CF_NullLast);
}
static inline void mke_set_not_null(MKEntry *e)
{
i_clear_flag(&e->compflags, MKE_CF_NULLBITS);
i_set_flag(&e->compflags, MKE_CF_NotNull);
}
static inline bool mke_is_null(MKEntry *e)
{
return (e->compflags & MKE_CF_NULLBITS) != MKE_CF_NotNull;
}
static inline int32 mke_get_nullbits(MKEntry *e)
{
return (e->compflags & MKE_CF_NULLBITS);
}
static inline int32 mke_get_run(MKEntry *e)
{
return i_get_flag(&e->compflags, MKE_CF_Run, MKE_RunShift);
}
static inline void mke_set_run(MKEntry *e, int32 run)
{
Assert(run >= 0 && run <= MKE_MAX_RUN);
/* If statement_mem is too small, we might end up generating
too many runs. */
if (run > MKE_MAX_RUN)
{
elog(ERROR, ERRMSG_GP_INSUFFICIENT_STATEMENT_MEMORY);
}
i_clear_flag(&e->compflags, MKE_CF_Run);
i_set_flag(&e->compflags, run << MKE_RunShift);
}
static inline int32 mke_get_lv(MKEntry *e)
{
return (MKE_MAX_LV - i_get_flag(&e->compflags, MKE_CF_Lv, MKE_LvShift));
}
static inline void mke_set_lv(MKEntry *e, int32 lv)
{
Assert(lv >= 0 && lv <= MKE_MAX_LV);
/* If statement_mem is too small, we might end up generating
too many runs. */
if (lv > MKE_MAX_LV)
{
elog(ERROR, ERRMSG_GP_INSUFFICIENT_STATEMENT_MEMORY);
}
i_clear_flag(&e->compflags, MKE_CF_Lv);
i_set_flag(&e->compflags, (MKE_MAX_LV - lv) << MKE_LvShift);
}
static inline void mke_set_empty(MKEntry *e)
{
e->compflags = MKE_CF_Empty;
e->flags = 0;
e->d = 0;
e->ptr = 0;
}
static inline bool mke_is_empty(MKEntry *e)
{
return i_test_flag(&e->compflags, MKE_CF_Empty);
}
static inline void mke_set_comp_max(MKEntry *e)
{
e->compflags = MKE_CF_MAX;
}
static inline void mke_set_refc(MKEntry *e)
{
i_set_flag(&e->flags, MKE_F_RefCnt);
}
static inline void mke_clear_refc(MKEntry *e)
{
i_clear_flag(&e->flags, MKE_F_RefCnt);
}
static inline bool mke_is_refc(MKEntry *e)
{
return i_test_flag(&e->flags, MKE_F_RefCnt);
}
static inline void mke_set_copied(MKEntry *e)
{
i_set_flag(&e->flags, MKE_F_Copied);
}
static inline void mke_clear_copied(MKEntry *e)
{
i_clear_flag(&e->flags, MKE_F_Copied);
}
static inline bool mke_is_copied(MKEntry *e)
{
return i_test_flag(&e->flags, MKE_F_Copied);
}
static inline void mke_clear_refc_copied(MKEntry *e)
{
i_clear_flag(&e->flags, MKE_F_RefCnt | MKE_F_Copied);
}
static inline int32 mke_get_reader(MKEntry *e)
{
return i_get_flag(&e->flags, MKE_F_Reader, MKE_ReaderShift);
}
static inline void mke_set_reader(MKEntry *e, int32 r)
{
Assert(r >= 0 && r <= MKE_MAX_READER);
i_clear_flag(&e->flags, MKE_F_Reader);
i_set_flag(&e->flags, r << MKE_ReaderShift);
}
struct MKContext;
struct MKLvContext;
typedef int32 (*MKCompare) (MKEntry *v1, MKEntry *v2, struct MKLvContext *lvctxt, struct MKContext *mkctxt);
typedef void (*MKCopyFree) (MKEntry *dst, MKEntry *src, struct MKLvContext *lvctxt);
typedef Datum (*MKFetchDatumForPrepare) (MKEntry *a, struct MKContext *mkctxt, struct MKLvContext *lvctxt, bool *isNullOut);
typedef void (*MKFreeTuple) (MKEntry *e);
extern void tupsort_prepare(MKEntry *a, struct MKContext *ctxt, int lv);
/*
* Our impl. of multi-key sort and heap only handles the following data type.
*/
typedef enum MKLvType
{
MKLV_TYPE_NONE, /* this level has not yet been assigned a type: todo: verify meaning */
MKLV_TYPE_INT32, /* this level contains int32 values */
MKLV_TYPE_CHAR, /* this level contains char (blank padded) values */
MKLV_TYPE_TEXT, /* this level contains text values */
} MKLvType;
typedef struct MKLvContext
{
/* Is the type of datums in this level passed by value instead of reference */
bool typByVal;
/* what is the length field of datums in this level */
int typLen;
/* type of datums in this level, converted to our MKLvType enumeration */
MKLvType lvtype;
SortFunctionKind sortfnkind;
FmgrInfo fmgrinfo;
/* should null sort first (low) in this level */
bool nullfirst;
int16 attno;
/* the mk heap context that this level context belongs to */
struct MKContext *mkctxt;
} MKLvContext;
typedef struct MKContext {
/* how many levels are there? This should correspond to the # of keys in the multi-key value */
int total_lv;
/* the levels themselves */
MKLvContext *lvctxt;
/* callback capable of fetching a datum from the MKEntry data */
MKFetchDatumForPrepare fetchForPrep;
/* Callback capable of copying prepared data from one MKEntry to another (freeing the dest MKEntry).
* It can also be called with a NULL src so that the dst is simply freed.
*/
MKCopyFree cpfr;
/**
* MUST be set
*
* pfree() the out-of-line data (not the SortTuple struct!), and increase
* state->availMem by the amount of memory space thereby released.
*/
MKFreeTuple freeTup;
/* as calculated by estimateExtraSpace. This is only valid before we've switched to buildruns mode
* It is only calculate when the fetchForPrep fn is set as well
*/
long estimatedExtraForPrep;
/* strxfrm's scale factor (multiplies by length), depends on the current collation */
int strxfrmScaleFactor;
/* strxfrm's constant factor, depends on the current collation */
int strxfrmConstantFactor;
TupleDesc tupdesc;
MemTupleBinding *mt_bind;
/* Limit the sort? If 0 then we sort all input values, else we keep only the first limit-many values */
int32 limit;
/* Bit trick: this mask will be set so it can be used to negate a return value. See comments on usage */
int32 limitmask;
/* Unique sort: should the sort discard duplicate values? */
bool unique;
/* enforce Unique, for index build */
bool enforceUnique;
} MKContext;
/**
* This prepares entries AND sets their level.
*/
static inline void mk_prepare_array(MKEntry *a, int i, int j, int32 lv, struct MKContext *ctxt)
{
MKEntry *last = a+j;
MKEntry *cur = a+i;
Assert(a && j>=0);
do {
if (ctxt->fetchForPrep)
tupsort_prepare(cur, ctxt, lv);
mke_set_lv(cur, lv);
} while (++cur <= last);
}
extern void tupsort_cpfr(MKEntry *dst, MKEntry *src, MKLvContext *ctxt);
extern int tupsort_compare_datum(MKEntry *v1, MKEntry *v2, MKLvContext *ctxt, MKContext *mkContext);
extern void create_mksort_context(
MKContext *mkctxt,
int nkeys,
MKFetchDatumForPrepare fetchForPrep, MKFreeTuple freeTup, TupleDesc tupdesc, bool tbyv, int tlen,
Oid *sortOperators,
AttrNumber *attNums,
ScanKey sk);
/* MK quicksort stuff */
extern void mk_qsort_impl(MKEntry *a, int left, int right, int lv, bool lvdown, MKContext *ctxt, bool seenNull);
static inline void mk_qsort(MKEntry* a, int n, MKContext *ctxt)
{
mk_qsort_impl(a, 0, n-1, 0, true, ctxt, false);
}
/* MK Heap stuff */
typedef bool (*MKFlagPtrReader) (void *ctxt, MKEntry *e);
typedef struct MKHeapReader
{
MKFlagPtrReader reader;
void *mkhr_ctxt;
} MKHeapReader;
/**
* The heap itself
*/
typedef struct MKHeap
{
/* Configuration data for the heap, indicating how to interpret values and how to perform the sort */
MKContext *mkctxt;
MKEntry *lvtops;
/* the top of the heap */
MKEntry *p;
/* the number of non-empty values in the heap */
int count;
/* the first maxentry values of p represent the heap, which contains cnt # of non-empty values. cnt <= maxentry
*
* todo: rename this to numEntries (or maxentryplusone)
*/
int maxentry;
/* the allocated size of p, can be used to check for overruns */
int alloc_size;
/* if 0 then we have an instantiated heap
* If non-zero then we have a heap which contains only the most-recent values from each reader. When a
* value is removed from the heap it will be replenished, if possible, by a value from the reader
* that produced the value.
*/
int nreader;
/* the readers themselves -- see nreader field */
MKHeapReader *readers;
} MKHeap;
/*
* Create a heap from an array. Once the heap is created, it owns the array.
*/
extern MKHeap *mkheap_from_array(MKEntry *entries, int alloc_size, int count, MKContext *mkctxt);
/*
* Create a heap from an array of readers. MKHeap never owns the reader. Readers
* are assumed to have a longer life cycle than the mkheap.
*/
extern MKHeap *mkheap_from_reader(MKHeapReader *readers, int nreader, MKContext *mkctxt);
extern void mkheap_destroy(MKHeap *mkheap);
/*
* Insert into heap.
* NOTE: Unusual interfaces.
* insert an flagptr.
* return value < 0 means fail to insert because the value is smaller than the smallest entry in heap.
* return value = 0 means fail to insert because the value equals to smallest entry in heap.
* return value > 0 means successfully inserted into the heap.
*
* This means, we cannot insert anything smaller than current smallest entry, however, we will see this
* is enough for our use in external sort or limit sort. More general way of doing this is definitely
* doable, but not needed.
*
* On output, in cases where the value is >= the min element, *e will be filled in with a copy of the min value of the heap.
*/
extern int mkheap_putAndGet(MKHeap *mkheap, MKEntry *e);
extern int mkheap_putAndGet_run(MKHeap *mkheap, MKEntry *e, int16 run);
extern int mkheap_putAndGet_reader(MKHeap *mkheap, MKEntry *e);
/*
* Remove and return smallest elements from the heap.
*/
extern MKEntry *mkheap_peek(MKHeap *mkheap);
static inline bool mkheap_run_match(MKHeap *mkheap, int32 run)
{
MKEntry *e = mkheap_peek(mkheap);
if(e)
return (run == mke_get_run(e));
else
return false;
}
static inline bool mkheap_empty(MKHeap *mkheap)
{
return mkheap->count == 0;
}
static inline int64 mkheap_cnt(MKHeap *mkheap)
{
return mkheap->count;
}
/**
* ereport an ERROR indicating that the uniqueness constraint was violated
*/
#define ERROR_UNIQUENESS_VIOLATED() \
do \
{ \
ereport(ERROR, (errcode(ERRCODE_UNIQUE_VIOLATION), \
errmsg("could not create unique index"), \
errdetail("Table contains duplicate values."))); \
} while(0)
#endif