📄 numeric.c
字号:
init_var(&result); set_var_from_str(buf, &result); res = make_result(&result); free_var(&result); PG_RETURN_NUMERIC(res);}Datumnumeric_float4(PG_FUNCTION_ARGS){ Numeric num = PG_GETARG_NUMERIC(0); char *tmp; Datum result; if (NUMERIC_IS_NAN(num)) PG_RETURN_FLOAT4((float4) NAN); tmp = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num))); result = DirectFunctionCall1(float4in, CStringGetDatum(tmp)); pfree(tmp); PG_RETURN_DATUM(result);}Datumtext_numeric(PG_FUNCTION_ARGS){ text *str = PG_GETARG_TEXT_P(0); int len; char *s; Datum result; len = (VARSIZE(str) - VARHDRSZ); s = palloc(len + 1); memcpy(s, VARDATA(str), len); *(s + len) = '\0'; result = DirectFunctionCall3(numeric_in, CStringGetDatum(s), ObjectIdGetDatum(0), Int32GetDatum(-1)); pfree(s); return result;}Datumnumeric_text(PG_FUNCTION_ARGS){ /* val is numeric, but easier to leave it as Datum */ Datum val = PG_GETARG_DATUM(0); char *s; int len; text *result; s = DatumGetCString(DirectFunctionCall1(numeric_out, val)); len = strlen(s); result = (text *) palloc(VARHDRSZ + len); VARATT_SIZEP(result) = len + VARHDRSZ; memcpy(VARDATA(result), s, len); pfree(s); PG_RETURN_TEXT_P(result);}/* ---------------------------------------------------------------------- * * Aggregate functions * * The transition datatype for all these aggregates is a 3-element array * of Numeric, holding the values N, sum(X), sum(X*X) in that order. * * We represent N as a numeric mainly to avoid having to build a special * datatype; it's unlikely it'd overflow an int4, but ... * * ---------------------------------------------------------------------- */static ArrayType *do_numeric_accum(ArrayType *transarray, Numeric newval){ Datum *transdatums; int ndatums; Datum N, sumX, sumX2; ArrayType *result; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, &ndatums); if (ndatums != 3) elog(ERROR, "expected 3-element numeric array"); N = transdatums[0]; sumX = transdatums[1]; sumX2 = transdatums[2]; N = DirectFunctionCall1(numeric_inc, N); sumX = DirectFunctionCall2(numeric_add, sumX, NumericGetDatum(newval)); sumX2 = DirectFunctionCall2(numeric_add, sumX2, DirectFunctionCall2(numeric_mul, NumericGetDatum(newval), NumericGetDatum(newval))); transdatums[0] = N; transdatums[1] = sumX; transdatums[2] = sumX2; result = construct_array(transdatums, 3, NUMERICOID, -1, false, 'i'); return result;}Datumnumeric_accum(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Numeric newval = PG_GETARG_NUMERIC(1); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));}/* * Integer data types all use Numeric accumulators to share code and * avoid risk of overflow. For int2 and int4 inputs, Numeric accumulation * is overkill for the N and sum(X) values, but definitely not overkill * for the sum(X*X) value. Hence, we use int2_accum and int4_accum only * for stddev/variance --- there are faster special-purpose accumulator * routines for SUM and AVG of these datatypes. */Datumint2_accum(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval2 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2)); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));}Datumint4_accum(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval4 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4)); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));}Datumint8_accum(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Datum newval8 = PG_GETARG_DATUM(1); Numeric newval; newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8)); PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));}Datumnumeric_avg(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Datum *transdatums; int ndatums; Numeric N, sumX; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, &ndatums); if (ndatums != 3) elog(ERROR, "expected 3-element numeric array"); N = DatumGetNumeric(transdatums[0]); sumX = DatumGetNumeric(transdatums[1]); /* ignore sumX2 */ /* SQL92 defines AVG of no values to be NULL */ /* N is zero iff no digits (cf. numeric_uminus) */ if (N->varlen == NUMERIC_HDRSZ) PG_RETURN_NULL(); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, NumericGetDatum(sumX), NumericGetDatum(N)));}Datumnumeric_variance(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Datum *transdatums; int ndatums; Numeric N, sumX, sumX2, res; NumericVar vN, vsumX, vsumX2, vNminus1; int rscale; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, &ndatums); if (ndatums != 3) elog(ERROR, "expected 3-element numeric array"); N = DatumGetNumeric(transdatums[0]); sumX = DatumGetNumeric(transdatums[1]); sumX2 = DatumGetNumeric(transdatums[2]); if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2)) PG_RETURN_NUMERIC(make_result(&const_nan)); /* Sample variance is undefined when N is 0 or 1, so return NULL */ init_var(&vN); set_var_from_num(N, &vN); if (cmp_var(&vN, &const_one) <= 0) { free_var(&vN); PG_RETURN_NULL(); } init_var(&vNminus1); sub_var(&vN, &const_one, &vNminus1); init_var(&vsumX); set_var_from_num(sumX, &vsumX); init_var(&vsumX2); set_var_from_num(sumX2, &vsumX2); /* compute rscale for mul_var calls */ rscale = vsumX.dscale * 2; mul_var(&vsumX, &vsumX, &vsumX, rscale); /* vsumX = sumX * sumX */ mul_var(&vN, &vsumX2, &vsumX2, rscale); /* vsumX2 = N * sumX2 */ sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ if (cmp_var(&vsumX2, &const_zero) <= 0) { /* Watch out for roundoff error producing a negative numerator */ res = make_result(&const_zero); } else { mul_var(&vN, &vNminus1, &vNminus1, 0); /* N * (N - 1) */ rscale = select_div_scale(&vsumX2, &vNminus1); div_var(&vsumX2, &vNminus1, &vsumX, rscale); /* variance */ res = make_result(&vsumX); } free_var(&vN); free_var(&vNminus1); free_var(&vsumX); free_var(&vsumX2); PG_RETURN_NUMERIC(res);}Datumnumeric_stddev(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Datum *transdatums; int ndatums; Numeric N, sumX, sumX2, res; NumericVar vN, vsumX, vsumX2, vNminus1; int rscale; /* We assume the input is array of numeric */ deconstruct_array(transarray, NUMERICOID, -1, false, 'i', &transdatums, &ndatums); if (ndatums != 3) elog(ERROR, "expected 3-element numeric array"); N = DatumGetNumeric(transdatums[0]); sumX = DatumGetNumeric(transdatums[1]); sumX2 = DatumGetNumeric(transdatums[2]); if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2)) PG_RETURN_NUMERIC(make_result(&const_nan)); /* Sample stddev is undefined when N is 0 or 1, so return NULL */ init_var(&vN); set_var_from_num(N, &vN); if (cmp_var(&vN, &const_one) <= 0) { free_var(&vN); PG_RETURN_NULL(); } init_var(&vNminus1); sub_var(&vN, &const_one, &vNminus1); init_var(&vsumX); set_var_from_num(sumX, &vsumX); init_var(&vsumX2); set_var_from_num(sumX2, &vsumX2); /* compute rscale for mul_var calls */ rscale = vsumX.dscale * 2; mul_var(&vsumX, &vsumX, &vsumX, rscale); /* vsumX = sumX * sumX */ mul_var(&vN, &vsumX2, &vsumX2, rscale); /* vsumX2 = N * sumX2 */ sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ if (cmp_var(&vsumX2, &const_zero) <= 0) { /* Watch out for roundoff error producing a negative numerator */ res = make_result(&const_zero); } else { mul_var(&vN, &vNminus1, &vNminus1, 0); /* N * (N - 1) */ rscale = select_div_scale(&vsumX2, &vNminus1); div_var(&vsumX2, &vNminus1, &vsumX, rscale); /* variance */ sqrt_var(&vsumX, &vsumX, rscale); /* stddev */ res = make_result(&vsumX); } free_var(&vN); free_var(&vNminus1); free_var(&vsumX); free_var(&vsumX2); PG_RETURN_NUMERIC(res);}/* * SUM transition functions for integer datatypes. * * To avoid overflow, we use accumulators wider than the input datatype. * A Numeric accumulator is needed for int8 input; for int4 and int2 * inputs, we use int8 accumulators which should be sufficient for practical * purposes. (The latter two therefore don't really belong in this file, * but we keep them here anyway.) * * Because SQL92 defines the SUM() of no values to be NULL, not zero, * the initial condition of the transition data value needs to be NULL. This * means we can't rely on ExecAgg to automatically insert the first non-null * data value into the transition data: it doesn't know how to do the type * conversion. The upshot is that these routines have to be marked non-strict * and handle substitution of the first non-null input themselves. */Datumint2_sum(PG_FUNCTION_ARGS){ int64 oldsum; int64 newval; if (PG_ARGISNULL(0)) { /* No non-null input seen so far... */ if (PG_ARGISNULL(1)) PG_RETURN_NULL(); /* still no non-null */ /* This is the first non-null input. */ newval = (int64) PG_GETARG_INT16(1); PG_RETURN_INT64(newval); } oldsum = PG_GETARG_INT64(0); /* Leave sum unchanged if new input is null. */ if (PG_ARGISNULL(1)) PG_RETURN_INT64(oldsum); /* OK to do the addition. */ newval = oldsum + (int64) PG_GETARG_INT16(1); PG_RETURN_INT64(newval);}Datumint4_sum(PG_FUNCTION_ARGS){ int64 oldsum; int64 newval; if (PG_ARGISNULL(0)) { /* No non-null input seen so far... */ if (PG_ARGISNULL(1)) PG_RETURN_NULL(); /* still no non-null */ /* This is the first non-null input. */ newval = (int64) PG_GETARG_INT32(1); PG_RETURN_INT64(newval); } oldsum = PG_GETARG_INT64(0); /* Leave sum unchanged if new input is null. */ if (PG_ARGISNULL(1)) PG_RETURN_INT64(oldsum); /* OK to do the addition. */ newval = oldsum + (int64) PG_GETARG_INT32(1); PG_RETURN_INT64(newval);}Datumint8_sum(PG_FUNCTION_ARGS){ Numeric oldsum; Datum newval; if (PG_ARGISNULL(0)) { /* No non-null input seen so far... */ if (PG_ARGISNULL(1)) PG_RETURN_NULL(); /* still no non-null */ /* This is the first non-null input. */ newval = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1)); PG_RETURN_DATUM(newval); } oldsum = PG_GETARG_NUMERIC(0); /* Leave sum unchanged if new input is null. */ if (PG_ARGISNULL(1)) PG_RETURN_NUMERIC(oldsum); /* OK to do the addition. */ newval = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(1)); PG_RETURN_DATUM(DirectFunctionCall2(numeric_add, NumericGetDatum(oldsum), newval));}/* * Routines for avg(int2) and avg(int4). The transition datatype * is a two-element int8 array, holding count and sum. */typedef struct Int8TransTypeData{#ifndef INT64_IS_BUSTED int64 count; int64 sum;#else /* "int64" isn't really 64 bits, so fake up properly-aligned fields */ int32 count; int32 pad1; int32 sum; int32 pad2;#endif} Int8TransTypeData;Datumint2_avg_accum(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); int16 newval = PG_GETARG_INT16(1); Int8TransTypeData *transdata; /* * We copied the input array, so it's okay to scribble on it directly. */ if (ARR_SIZE(transarray) != ARR_OVERHEAD(1) + sizeof(Int8TransTypeData)) elog(ERROR, "expected 2-element int8 array"); transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); transdata->count++; transdata->sum += newval; PG_RETURN_ARRAYTYPE_P(transarray);}Datumint4_avg_accum(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); int32 newval = PG_GETARG_INT32(1); Int8TransTypeData *transdata; /* * We copied the input array, so it's okay to scribble on it directly. */ if (ARR_SIZE(transarray) != ARR_OVERHEAD(1) + sizeof(Int8TransTypeData)) elog(ERROR, "expected 2-element int8 array"); transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); transdata->count++; transdata->sum += newval; PG_RETURN_ARRAYTYPE_P(transarray);}Datumint8_avg(PG_FUNCTION_ARGS){ ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); Int8TransTypeData *transdata; Datum countd, sumd; if (ARR_SIZE(transarray) != ARR_OVERHEAD(1) + sizeof(Int8TransTypeData)) elog(ERROR, "expected 2-element int8 array"); transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray); /* SQL92 defines AVG of no values to be NULL */ if (transdata->count == 0) PG_RETURN_NULL(); countd = DirectFunctionCall1(int8_numeric, Int64GetDatumFast(transdata->count)); sumd = DirectFunctionCall1(int8_numeric, Int64GetDatumFast(transdata->sum)); PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumd, countd));}/* ---------------------------------------------------------------------- * * Debug support * * ---------------------------------------------------------------------- */#ifdef NUMERIC_DEBUG/* * dump_numeric() - Dump a value in the db storage format for debugging */static voiddump_numeric(const char *str, Numeric num){ NumericDigit *digits = (NumericDigit *) num->n_data; int ndigits; int i; ndigits = (num->varlen - NUMERIC_HDRSZ) / sizeof(NumericDigit); printf("%s: NUMERIC w=%d d=%d ", str, num->n_weight, NUMERIC_DSCALE(num)); switch (NUMERIC_SIGN(num)) { case NUMERIC_POS: printf("POS"); break; case NUMERIC_NEG: printf("NEG"); break; case NUMERIC_NAN: printf("NaN"); break; default: printf("SIGN=0x%x", NUMERIC_SIGN(num)); break; } for (i = 0; i < ndigits; i++) printf(" %0*d", DEC_DIGITS, digits[i]); printf("\n");}/* * dump_var() - Dump a value in the variable format for debugging */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -