*** ./doc/src/sgml/func.sgml.orig 2010-09-09 02:48:22.000000000 +0200
--- ./doc/src/sgml/func.sgml 2010-09-25 21:22:01.493460314 +0200
***************
*** 10386,10391 ****
--- 10386,10411 ----
+
+ Arithmetic median
+ median
+
+ median(expression)
+
+
+ smallint, int,
+ bigint, real, double
+ precision, or numeric
+
+
+ double precision for floating-point arguments,
+ otherwise numeric
+
+ arithmetic median
+
+
+
+
regr_avgx(Y, X)
*** ./src/backend/utils/adt/median.c.orig 2010-09-25 22:23:01.000000000 +0200
--- ./src/backend/utils/adt/median.c 2010-09-26 12:16:40.798602994 +0200
***************
*** 0 ****
--- 1,649 ----
+ #include "postgres.h"
+
+ #include "fmgr.h"
+ #include "miscadmin.h"
+ #include
+
+ #include "access/nbtree.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_index.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
+ #include "utils/builtins.h"
+ #include "utils/datum.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/numeric.h"
+ #include "utils/tuplesort.h"
+
+ /*
+ * just memory storage - store a Datum to memory. Allows repetetive
+ * sort and append a new value.
+ */
+ typedef struct
+ {
+ void *tuple;
+ Datum datum1;
+ bool isnull1;
+ } SortTuple;
+
+ /*
+ * Simple sort storage - used just only Datums.
+ */
+ typedef struct
+ {
+ long allowedMem;
+ long availMem;
+ MemoryContext sortcontext;
+
+ SortTuple *memtuples;
+ int memtupcount;
+ int memtupsize;
+ int current;
+
+ Oid datumType;
+ FmgrInfo sortOpFn;
+ int sortFnFlags;
+ int datumTypLen;
+ bool datumTypByVal;
+ } Memsortstate;
+
+ /*
+ * used as type of state variable median's function. It uses a
+ * tuplesort as safe and inteligent storage. But we cannot to use
+ * a tuplesort under window aggregate context. Then we have to use a
+ * array of Datum. The API is similar to tuplesort API.
+ */
+ typedef struct
+ {
+ int nelems; /* number of valid entries */
+ bool use_tuplesort;
+ Tuplesortstate *sortstate;
+ Memsortstate *memsortstate;
+ FmgrInfo cast_func_finfo;
+ bool datumTypByVal;
+ } MedianAggState;
+
+ #define USEMEM(state, amt) ((state)->availMem -= (amt))
+ #define LACKMEM(state) ((state)->availMem < 0)
+ #define FREEMEM(state, amt) ((state)->availMem += (amt))
+
+ static int compare_datum(const SortTuple *a, const SortTuple *b, Memsortstate *state);
+
+
+ static Memsortstate *
+ memsort_begin_datum(Oid datumType,
+ Oid sortOperator, bool nullsFirstFlag,
+ int workMem)
+ {
+ MemoryContext sortcontext;
+ MemoryContext oldcontext;
+ bool reverse;
+ Oid sortFunction;
+ Memsortstate *state;
+ int16 typlen;
+ bool typbyval;
+
+ /*
+ * Create a working memory context for this sort operation. All
+ * data needed by the sort will live iside this context.
+ */
+ sortcontext = AllocSetContextCreate(CurrentMemoryContext,
+ "MemorySort",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(sortcontext);
+ state = (Memsortstate *) palloc0(sizeof(Memsortstate));
+
+ state->allowedMem = workMem * 1024L;
+ state->availMem = state->allowedMem;
+ state->memtupcount = 0;
+ state->memtupsize = 1024;
+ state->memtuples = (SortTuple*) palloc(state->memtupsize * sizeof(SortTuple));
+ state->sortcontext = sortcontext;
+
+ USEMEM(state, GetMemoryChunkSpace(state->memtuples));
+
+ if (LACKMEM(state))
+ elog(ERROR, "insufficient memory allowed for sort");
+
+ state->datumType = datumType;
+ if (!get_compare_function_for_ordering_op(sortOperator, &sortFunction, &reverse))
+ elog(ERROR, "operator %u is not a valid ordering operator",
+ sortOperator);
+ fmgr_info(sortFunction, &state->sortOpFn);
+
+ /* set ordering flags */
+ state->sortFnFlags = reverse ? SK_BT_DESC : 0;
+ if (nullsFirstFlag)
+ state->sortFnFlags |= SK_BT_NULLS_FIRST;
+
+ /* lookup necessary attributies of the datum type */
+ get_typlenbyval(datumType, &typlen, &typbyval);
+ state->datumTypLen = typlen;
+ state->datumTypByVal = typbyval;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return state;
+ }
+
+ static void
+ freeDatum(Datum value, bool typByVal, bool isNull)
+ {
+ if (!typByVal && !isNull)
+ {
+ Pointer s = DatumGetPointer(value);
+
+ pfree(s);
+ }
+ }
+
+ /*
+ * append a datum
+ */
+ static void
+ memsort_putdatum(Memsortstate *state, Datum val, bool isNull)
+ {
+ MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
+ SortTuple stup;
+
+ if (isNull || state->datumTypByVal)
+ {
+ stup.datum1 = val;
+ stup.isnull1 = isNull;
+ stup.tuple = NULL;
+ }
+ else
+ {
+ stup.datum1 = datumCopy(val, state->datumTypByVal, state->datumTypLen);
+ stup.isnull1 = false;
+ stup.tuple = DatumGetPointer(stup.datum1);
+ USEMEM(state, GetMemoryChunkSpace(stup.tuple));
+ }
+
+ if (state->memtupcount == state->memtupsize)
+ {
+ if (state->availMem <= (long) (state->memtupsize * sizeof(SortTuple)))
+ elog(ERROR, "unexpected out-of-memory situation during sort");
+ if ((Size) (state->memtupsize * 2) >= MaxAllocSize / sizeof(SortTuple))
+ elog(ERROR, "unexpected out-of-memory situation during sort");
+
+ FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
+ state->memtupsize *= 2;
+ state->memtuples = (SortTuple *)
+ repalloc(state->memtuples, state->memtupsize * sizeof(SortTuple));
+ USEMEM(state, GetMemoryChunkSpace(state->memtuples));
+ if (LACKMEM(state))
+ elog(ERROR, "unexpected out-of-memory situation during sort");
+ }
+
+ state->memtuples[state->memtupcount++] = stup;
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /*
+ * sort a current stored tuples
+ */
+ static void
+ memsort_performsort(Memsortstate *state)
+ {
+ if (state->memtupcount > 1)
+ qsort_arg((void *) state->memtuples,
+ state->memtupcount,
+ sizeof(SortTuple),
+ (qsort_arg_comparator) compare_datum,
+ (void *) state);
+
+ state->current = 0;
+ }
+
+ static bool
+ memsort_getdatum(Memsortstate *state,
+ Datum *val, bool *isNull)
+ {
+ SortTuple *stup;
+ MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
+
+ if (state->current < state->memtupcount)
+ {
+ stup = &state->memtuples[state->current++];
+ if (stup->isnull1 || state->datumTypByVal)
+ {
+ *val = stup->datum1;
+ *isNull = stup->isnull1;
+ }
+ else
+ {
+ *val = datumCopy(stup->datum1, false, state->datumTypLen);
+ *isNull = false;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return true;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return false;
+ }
+
+ /*
+ * Inline-able copy of FunctionCall2() to save some cycles in sorting.
+ */
+ static inline Datum
+ myFunctionCall2(FmgrInfo *flinfo, Datum arg1, Datum arg2)
+ {
+ FunctionCallInfoData fcinfo;
+ Datum result;
+
+ InitFunctionCallInfoData(fcinfo, flinfo, 2, NULL, NULL);
+
+ fcinfo.arg[0] = arg1;
+ fcinfo.arg[1] = arg2;
+ fcinfo.argnull[0] = false;
+ fcinfo.argnull[1] = false;
+
+ result = FunctionCallInvoke(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid);
+
+ return result;
+ }
+
+ /*
+ * Apply a sort function (by now converted to fmgr lookup form)
+ * and return a 3-way comparison result. This takes care of handling
+ * reverse-sort and NULLs-ordering properly. We assume that DESC and
+ * NULLS_FIRST options are encoded in sk_flags the same way btree does it.
+ */
+ static inline int32
+ inlineApplySortFunction(FmgrInfo *sortFunction, int sk_flags,
+ Datum datum1, bool isNull1,
+ Datum datum2, bool isNull2)
+ {
+ int32 compare;
+
+ if (isNull1)
+ {
+ if (isNull2)
+ compare = 0; /* NULL "=" NULL */
+ else if (sk_flags & SK_BT_NULLS_FIRST)
+ compare = -1; /* NULL "<" NOT_NULL */
+ else
+ compare = 1; /* NULL ">" NOT_NULL */
+ }
+ else if (isNull2)
+ {
+ if (sk_flags & SK_BT_NULLS_FIRST)
+ compare = 1; /* NOT_NULL ">" NULL */
+ else
+ compare = -1; /* NOT_NULL "<" NULL */
+ }
+ else
+ {
+ compare = DatumGetInt32(myFunctionCall2(sortFunction,
+ datum1, datum2));
+
+ if (sk_flags & SK_BT_DESC)
+ compare = -compare;
+ }
+
+ return compare;
+ }
+
+ static int
+ compare_datum(const SortTuple *a, const SortTuple *b, Memsortstate *state)
+ {
+ /* Allow interrupting long sorts */
+ CHECK_FOR_INTERRUPTS();
+
+ return inlineApplySortFunction(&state->sortOpFn, state->sortFnFlags,
+ a->datum1, a->isnull1,
+ b->datum1, b->isnull1);
+ }
+
+ static MedianAggState *
+ makeMedianAggState(FunctionCallInfo fcinfo, Oid valtype, Oid targettype)
+ {
+ MemoryContext oldctx;
+ MemoryContext aggcontext;
+ MedianAggState *aggstate;
+ Oid sortop,
+ castfunc;
+ CoercionPathType pathtype;
+ int16 typlen;
+ bool typbyval;
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ {
+ /* cannot be called directly because of internal-type argument */
+ elog(ERROR, "median_transfn called in non-aggregate context");
+ }
+
+ oldctx = MemoryContextSwitchTo(aggcontext);
+
+ aggstate = (MedianAggState *) palloc0(sizeof(MedianAggState));
+
+ valtype = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ get_sort_group_operators(valtype,
+ true, false, false,
+ &sortop, NULL, NULL);
+
+ /* lookup necessary attributies of the datum type, used for datumFree */
+ get_typlenbyval(valtype, &typlen, &typbyval);
+ aggstate->datumTypByVal = typbyval;
+
+ /*
+ * use a Datum mem sort insted a Tuplesort under windows aggregates
+ */
+ if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
+ {
+ aggstate->memsortstate = memsort_begin_datum(valtype,
+ sortop,
+ SORTBY_NULLS_DEFAULT,
+ work_mem);
+ aggstate->use_tuplesort = false;
+ }
+ else
+ {
+ aggstate->sortstate = tuplesort_begin_datum(valtype,
+ sortop,
+ SORTBY_NULLS_DEFAULT,
+ work_mem, true);
+ aggstate->use_tuplesort = true;
+ }
+
+ MemoryContextSwitchTo(oldctx);
+
+ if (valtype != targettype)
+ {
+ /* find a cast function */
+ pathtype = find_coercion_pathway(targettype, valtype,
+ COERCION_EXPLICIT,
+ &castfunc);
+ if (pathtype == COERCION_PATH_FUNC)
+ {
+ Assert(OidIsValid(castfunc));
+ fmgr_info_cxt(castfunc, &aggstate->cast_func_finfo,
+ aggcontext);
+ }
+ else if (pathtype == COERCION_PATH_RELABELTYPE)
+ {
+ aggstate->cast_func_finfo.fn_oid = InvalidOid;
+ }
+ else
+ elog(ERROR, "no conversion function from %s %s",
+ format_type_be(valtype),
+ format_type_be(targettype));
+ }
+
+ return aggstate;
+ }
+
+ static void
+ putdatum(MedianAggState *aggstate, Datum val)
+ {
+ if (aggstate->use_tuplesort)
+ {
+ Assert(aggstate->sortstate != NULL);
+ tuplesort_putdatum(aggstate->sortstate, val, false);
+ }
+ else
+ {
+ Assert(aggstate->memsortstate != NULL);
+ memsort_putdatum(aggstate->memsortstate, val, false);
+ }
+ }
+
+ static void
+ performsort(MedianAggState *aggstate)
+ {
+ if (aggstate->use_tuplesort)
+ {
+ Assert(aggstate->sortstate != NULL);
+ tuplesort_performsort(aggstate->sortstate);
+ }
+ else
+ {
+ Assert(aggstate->memsortstate != NULL);
+ memsort_performsort(aggstate->memsortstate);
+ }
+ }
+
+ static bool
+ getdatum(MedianAggState *aggstate,
+ Datum *value, bool *isNull)
+ {
+ if (aggstate->use_tuplesort)
+ {
+ Assert(aggstate->sortstate != NULL);
+ return tuplesort_getdatum(aggstate->sortstate,
+ true,
+ value, isNull);
+ }
+ else
+ {
+ Assert(aggstate->memsortstate != NULL);
+ return memsort_getdatum(aggstate->memsortstate,
+ value, isNull);
+ }
+ }
+
+ /*
+ * append a non NULL value to tuplesort
+ */
+ static Datum
+ common_median_transfn(FunctionCallInfo fcinfo, Oid typoid, Oid targetoid)
+ {
+ MedianAggState *aggstate;
+
+ aggstate = PG_ARGISNULL(0) ? NULL : (MedianAggState *) PG_GETARG_POINTER(0);
+
+ if (!PG_ARGISNULL(1))
+ {
+ if (aggstate == NULL)
+ aggstate = makeMedianAggState(fcinfo, typoid, targetoid);
+
+ putdatum(aggstate, PG_GETARG_DATUM(1));
+ aggstate->nelems++;
+ }
+
+ PG_RETURN_POINTER(aggstate);
+ }
+
+ /*
+ * just wrappers to be opr sanity checks happy
+ */
+ Datum
+ median_numeric_transfn(PG_FUNCTION_ARGS)
+ {
+ return common_median_transfn(fcinfo,
+ NUMERICOID, NUMERICOID);
+ }
+
+ Datum
+ median_int8_transfn(PG_FUNCTION_ARGS)
+ {
+ return common_median_transfn(fcinfo,
+ INT8OID, NUMERICOID);
+ }
+
+ Datum
+ median_int4_transfn(PG_FUNCTION_ARGS)
+ {
+ return common_median_transfn(fcinfo,
+ INT4OID, NUMERICOID);
+ }
+
+ Datum
+ median_int2_transfn(PG_FUNCTION_ARGS)
+ {
+ return common_median_transfn(fcinfo,
+ INT2OID, NUMERICOID);
+ }
+
+ Datum
+ median_double_transfn(PG_FUNCTION_ARGS)
+ {
+ return common_median_transfn(fcinfo,
+ FLOAT8OID, FLOAT8OID);
+ }
+
+ Datum
+ median_float_transfn(PG_FUNCTION_ARGS)
+ {
+ return common_median_transfn(fcinfo,
+ FLOAT4OID, FLOAT8OID);
+ }
+
+ /*
+ * Used for reading values from tuplesort. The value has to be
+ * double or cast function is defined (and used).
+ */
+ static double
+ to_double(Datum value, FmgrInfo *cast_func_finfo)
+ {
+ /* when valtype is same as target type, returns directly */
+ if (cast_func_finfo->fn_oid == InvalidOid)
+ return DatumGetFloat8(value);
+
+ return DatumGetFloat8(FunctionCall1(cast_func_finfo, value));
+ }
+
+ /*
+ * Used as final function for median when result is double.
+ */
+ Datum
+ median_finalfn_double(PG_FUNCTION_ARGS)
+ {
+ MedianAggState *aggstate;
+
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ aggstate = PG_ARGISNULL(0) ? NULL : (MedianAggState *) PG_GETARG_POINTER(0);
+
+ if (aggstate != NULL)
+ {
+ int lidx;
+ int hidx;
+ Datum value;
+ bool isNull;
+ int i = 1;
+ double result = 0;
+
+ hidx = aggstate->nelems / 2 + 1;
+ lidx = (aggstate->nelems + 1) / 2;
+
+ performsort(aggstate);
+
+ while (getdatum(aggstate, &value, &isNull))
+ {
+ if (i++ == lidx)
+ {
+ result = to_double(value, &aggstate->cast_func_finfo);
+ freeDatum(value, aggstate->datumTypByVal, isNull);
+
+ if (lidx != hidx)
+ {
+ getdatum(aggstate, &value, &isNull);
+ result = (result + to_double(value, &aggstate->cast_func_finfo)) / 2.0;
+ freeDatum(value, aggstate->datumTypByVal, isNull);
+ }
+ break;
+ }
+ }
+
+ /* only for tuplesort call a end function */
+ if (aggstate->use_tuplesort)
+ tuplesort_end(aggstate->sortstate);
+
+ PG_RETURN_FLOAT8(result);
+ }
+ else
+ PG_RETURN_NULL();
+ }
+
+ /*
+ * Used for reading values from tuplesort. The value has to be
+ * Numeric or cast function is defined (and used).
+ */
+ static Numeric
+ to_numeric(Datum value, FmgrInfo *cast_func_finfo)
+ {
+ /* when valtype is same as target type, returns directly */
+ if (cast_func_finfo->fn_oid == InvalidOid)
+ return DatumGetNumeric(value);
+
+ return DatumGetNumeric(FunctionCall1(cast_func_finfo, value));
+ }
+
+ /*
+ * Used as final function for median when result is numeric.
+ */
+ Datum
+ median_finalfn_numeric(PG_FUNCTION_ARGS)
+ {
+ MedianAggState *aggstate;
+
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ aggstate = PG_ARGISNULL(0) ? NULL : (MedianAggState *) PG_GETARG_POINTER(0);
+
+ if (aggstate != NULL)
+ {
+ int lidx;
+ int hidx;
+ Datum a_value;
+ bool a_isNull;
+ int i = 1;
+ Numeric result = NULL; /* be compiler quiet */
+
+ hidx = aggstate->nelems / 2 + 1;
+ lidx = (aggstate->nelems + 1) / 2;
+
+ performsort(aggstate);
+
+ while (getdatum(aggstate, &a_value, &a_isNull))
+ {
+ if (i++ == lidx)
+ {
+ result = to_numeric(a_value, &aggstate->cast_func_finfo);
+
+ if (lidx != hidx)
+ {
+ Datum b_value;
+ bool b_isNull;
+ Numeric stack;
+
+ getdatum(aggstate, &b_value, &b_isNull);
+ stack = to_numeric(b_value, &aggstate->cast_func_finfo);
+
+ stack = DatumGetNumeric(DirectFunctionCall2(numeric_add,
+ NumericGetDatum(stack),
+ NumericGetDatum(result)));
+ result = DatumGetNumeric(DirectFunctionCall2(numeric_div,
+ NumericGetDatum(stack),
+ DirectFunctionCall1(float4_numeric,
+ Float4GetDatum(2.0))));
+ freeDatum(b_value, aggstate->datumTypByVal, b_isNull);
+ }
+ break;
+ }
+ else
+ freeDatum(a_value, aggstate->datumTypByVal, a_isNull);
+ }
+
+ /* only for tuplesort call a end function */
+ if (aggstate->use_tuplesort)
+ tuplesort_end(aggstate->sortstate);
+
+ PG_RETURN_NUMERIC(result);
+ }
+ else
+ PG_RETURN_NULL();
+ }
*** ./src/include/catalog/pg_aggregate.h.orig 2010-09-25 21:21:11.081451603 +0200
--- ./src/include/catalog/pg_aggregate.h 2010-09-25 21:22:01.497461866 +0200
***************
*** 226,231 ****
--- 226,239 ----
/* text */
DATA(insert ( 3538 string_agg_transfn string_agg_finalfn 0 2281 _null_ ));
+ /* median */
+ DATA(insert ( 3123 median_double_transfn median_finalfn_double 0 2281 _null_ ));
+ DATA(insert ( 3124 median_float_transfn median_finalfn_double 0 2281 _null_ ));
+ DATA(insert ( 3125 median_numeric_transfn median_finalfn_numeric 0 2281 _null_ ));
+ DATA(insert ( 3126 median_int8_transfn median_finalfn_numeric 0 2281 _null_ ));
+ DATA(insert ( 3127 median_int4_transfn median_finalfn_numeric 0 2281 _null_ ));
+ DATA(insert ( 3128 median_int2_transfn median_finalfn_numeric 0 2281 _null_ ));
+
/*
* prototypes for functions in pg_aggregate.c
*/
*** ./src/include/catalog/pg_proc.h.orig 2010-09-03 03:34:55.000000000 +0200
--- ./src/include/catalog/pg_proc.h 2010-09-25 21:22:01.500452259 +0200
***************
*** 4850,4855 ****
--- 4850,4884 ----
DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ ));
DESCR("fetch the Nth row value");
+ /* median aggregate */
+ DATA(insert OID = 3115 ( median_numeric_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ median_numeric_transfn _null_ _null_ _null_ ));
+ DESCR("median transident function for numeric");
+ DATA(insert OID = 3116 ( median_int8_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ median_int8_transfn _null_ _null_ _null_ ));
+ DESCR("median transident function for bigint");
+ DATA(insert OID = 3117 ( median_int4_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 23" _null_ _null_ _null_ _null_ median_int4_transfn _null_ _null_ _null_ ));
+ DESCR("median transident function for int");
+ DATA(insert OID = 3118 ( median_int2_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 21" _null_ _null_ _null_ _null_ median_int2_transfn _null_ _null_ _null_ ));
+ DESCR("median transident function for smallint");
+ DATA(insert OID = 3119 ( median_double_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 701" _null_ _null_ _null_ _null_ median_double_transfn _null_ _null_ _null_ ));
+ DESCR("median transident function for double precision");
+ DATA(insert OID = 3120 ( median_float_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 700" _null_ _null_ _null_ _null_ median_float_transfn _null_ _null_ _null_ ));
+ DESCR("median transident function for real");
+ DATA(insert OID = 3121 ( median_finalfn_double PGNSP PGUID 12 1 0 0 f f f f f i 1 0 701 "2281" _null_ _null_ _null_ _null_ median_finalfn_double _null_ _null_ _null_ ));
+ DESCR("median final function with double precision result");
+ DATA(insert OID = 3122 ( median_finalfn_numeric PGNSP PGUID 12 1 0 0 f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ median_finalfn_numeric _null_ _null_ _null_ ));
+ DESCR("median final function with numeric result");
+ DATA(insert OID = 3123 ( median PGNSP PGUID 12 1 0 0 t f f f f i 1 0 701 "701" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("median aggregate");
+ DATA(insert OID = 3124 ( median PGNSP PGUID 12 1 0 0 t f f f f i 1 0 701 "700" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("median aggregate");
+ DATA(insert OID = 3125 ( median PGNSP PGUID 12 1 0 0 t f f f f i 1 0 1700 "1700" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("median aggregate");
+ DATA(insert OID = 3126 ( median PGNSP PGUID 12 1 0 0 t f f f f i 1 0 1700 "20" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("median aggregate");
+ DATA(insert OID = 3127 ( median PGNSP PGUID 12 1 0 0 t f f f f i 1 0 1700 "23" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("median aggregate");
+ DATA(insert OID = 3128 ( median PGNSP PGUID 12 1 0 0 t f f f f i 1 0 1700 "21" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("median aggregate");
/*
* Symbolic values for provolatile column: these indicate whether the result
*** ./src/include/utils/builtins.h.orig 2010-09-03 03:34:55.000000000 +0200
--- ./src/include/utils/builtins.h 2010-09-25 21:22:01.530706224 +0200
***************
*** 934,939 ****
--- 934,947 ----
extern Datum int8_avg(PG_FUNCTION_ARGS);
extern Datum width_bucket_numeric(PG_FUNCTION_ARGS);
extern Datum hash_numeric(PG_FUNCTION_ARGS);
+ extern Datum median_numeric_transfn(PG_FUNCTION_ARGS);
+ extern Datum median_int8_transfn(PG_FUNCTION_ARGS);
+ extern Datum median_int4_transfn(PG_FUNCTION_ARGS);
+ extern Datum median_int2_transfn(PG_FUNCTION_ARGS);
+ extern Datum median_double_transfn(PG_FUNCTION_ARGS);
+ extern Datum median_float_transfn(PG_FUNCTION_ARGS);
+ extern Datum median_finalfn_double(PG_FUNCTION_ARGS);
+ extern Datum median_finalfn_numeric(PG_FUNCTION_ARGS);
/* ri_triggers.c */
extern Datum RI_FKey_check_ins(PG_FUNCTION_ARGS);
*** ./src/test/regress/expected/numeric.out.orig 2010-09-25 21:21:11.086476751 +0200
--- ./src/test/regress/expected/numeric.out 2010-09-26 12:27:45.000000000 +0200
***************
*** 1372,1374 ****
--- 1372,1423 ----
12345678901234567890
(1 row)
+ -- median test
+ create table median_test (a int, b bigint, c smallint, d numeric, e double precision, f real);
+ insert into median_test select i,i,i,i,i,i from generate_series(1,10) g(i);
+ select median(a),
+ median(b),
+ median(c),
+ median(d),
+ median(e),
+ median(f) from median_test;
+ median | median | median | median | median | median
+ --------------------+--------------------+--------------------+--------------------+--------+--------
+ 5.5000000000000000 | 5.5000000000000000 | 5.5000000000000000 | 5.5000000000000000 | 5.5 | 5.5
+ (1 row)
+
+ truncate table median_test;
+ insert into median_test select i,i,i,i,i,i from generate_series(1,11) g(i);
+ select median(a),
+ median(b),
+ median(c),
+ median(d),
+ median(e),
+ median(f) from median_test;
+ median | median | median | median | median | median
+ --------+--------+--------+--------+--------+--------
+ 6 | 6 | 6 | 6 | 6 | 6
+ (1 row)
+
+ select median(a) over (order by a),
+ median(b) over (order by a),
+ median(c) over (order by a),
+ median(d) over (order by a),
+ median(e) over (order by a),
+ median(f) over (order by a) from median_test;
+ median | median | median | median | median | median
+ --------------------+--------------------+--------------------+--------------------+--------+--------
+ 1 | 1 | 1 | 1 | 1 | 1
+ 1.5000000000000000 | 1.5000000000000000 | 1.5000000000000000 | 1.5000000000000000 | 1.5 | 1.5
+ 2 | 2 | 2 | 2 | 2 | 2
+ 2.5000000000000000 | 2.5000000000000000 | 2.5000000000000000 | 2.5000000000000000 | 2.5 | 2.5
+ 3 | 3 | 3 | 3 | 3 | 3
+ 3.5000000000000000 | 3.5000000000000000 | 3.5000000000000000 | 3.5000000000000000 | 3.5 | 3.5
+ 4 | 4 | 4 | 4 | 4 | 4
+ 4.5000000000000000 | 4.5000000000000000 | 4.5000000000000000 | 4.5000000000000000 | 4.5 | 4.5
+ 5 | 5 | 5 | 5 | 5 | 5
+ 5.5000000000000000 | 5.5000000000000000 | 5.5000000000000000 | 5.5000000000000000 | 5.5 | 5.5
+ 6 | 6 | 6 | 6 | 6 | 6
+ (11 rows)
+
+ drop table median_test;
*** ./src/test/regress/sql/numeric.sql.orig 2010-09-25 21:21:11.087459138 +0200
--- ./src/test/regress/sql/numeric.sql 2010-09-26 12:27:12.226352249 +0200
***************
*** 824,826 ****
--- 824,852 ----
select 12345678901234567890 / 123;
select div(12345678901234567890, 123);
select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
+
+ -- median test
+ create table median_test (a int, b bigint, c smallint, d numeric, e double precision, f real);
+ insert into median_test select i,i,i,i,i,i from generate_series(1,10) g(i);
+ select median(a),
+ median(b),
+ median(c),
+ median(d),
+ median(e),
+ median(f) from median_test;
+ truncate table median_test;
+ insert into median_test select i,i,i,i,i,i from generate_series(1,11) g(i);
+ select median(a),
+ median(b),
+ median(c),
+ median(d),
+ median(e),
+ median(f) from median_test;
+ select median(a) over (order by a),
+ median(b) over (order by a),
+ median(c) over (order by a),
+ median(d) over (order by a),
+ median(e) over (order by a),
+ median(f) over (order by a) from median_test;
+
+ drop table median_test;