📄 numeric.c
字号:
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 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); } /* * If we're invoked by nodeAgg, we can cheat and modify out first * parameter in-place to avoid palloc overhead. If not, we need to return * the new value of the transition variable. */ if (fcinfo->context && IsA(fcinfo->context, AggState)) { int64 *oldsum = (int64 *) PG_GETARG_POINTER(0); /* Leave the running sum unchanged in the new input is null */ if (!PG_ARGISNULL(1)) *oldsum = *oldsum + (int64) PG_GETARG_INT16(1); PG_RETURN_POINTER(oldsum); } else { int64 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 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); } /* * If we're invoked by nodeAgg, we can cheat and modify out first * parameter in-place to avoid palloc overhead. If not, we need to return * the new value of the transition variable. */ if (fcinfo->context && IsA(fcinfo->context, AggState)) { int64 *oldsum = (int64 *) PG_GETARG_POINTER(0); /* Leave the running sum unchanged in the new input is null */ if (!PG_ARGISNULL(1)) *oldsum = *oldsum + (int64) PG_GETARG_INT32(1); PG_RETURN_POINTER(oldsum); } else { int64 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); } /* * Note that we cannot special-case the nodeAgg case here, as we do for * int2_sum and int4_sum: numeric is of variable size, so we cannot modify * our first parameter in-place. */ 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; int16 newval = PG_GETARG_INT16(1); Int8TransTypeData *transdata; /* * If we're invoked by nodeAgg, we can cheat and modify our first * parameter in-place to reduce palloc overhead. Otherwise we need to make * a copy of it before scribbling on it. */ if (fcinfo->context && IsA(fcinfo->context, AggState)) transarray = PG_GETARG_ARRAYTYPE_P(0); else transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); 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; int32 newval = PG_GETARG_INT32(1); Int8TransTypeData *transdata; /* * If we're invoked by nodeAgg, we can cheat and modify our first * parameter in-place to reduce palloc overhead. Otherwise we need to make * a copy of it before scribbling on it. */ if (fcinfo->context && IsA(fcinfo->context, AggState)) transarray = PG_GETARG_ARRAYTYPE_P(0); else transarray = PG_GETARG_ARRAYTYPE_P_COPY(0); 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 */static voiddump_var(const char *str, NumericVar *var){ int i; printf("%s: VAR w=%d d=%d ", str, var->weight, var->dscale); switch (var->sign) { case NUMERIC_POS: printf("POS"); break; case NUMERIC_NEG: printf("NEG"); break; case NUMERIC_NAN: printf("NaN"); break; default: printf("SIGN=0x%x", var->sign); break; } for (i = 0; i < var->ndigits; i++) printf(" %0*d", DEC_DIGITS, var->digits[i]); printf("\n");}#endif /* NUMERIC_DEBUG *//* ---------------------------------------------------------------------- * * Local functions follow * * In general, these do not support NaNs --- callers must eliminate * the possibility of NaN first. (make_result() is an exception.) * * ---------------------------------------------------------------------- *//* * alloc_var() - * * Allocate a digit buffer of ndigits digits (plus a spare digit for rounding) */static voidalloc_var(NumericVar *var, int ndigits){ digitbuf_free(var->buf); var->buf = digitbuf_alloc(ndigits + 1); var->buf[0] = 0; /* spare digit for rounding */ var->digits = var->buf + 1; var->ndigits = ndigits;}/* * free_var() - * * Return the digit buffer of a variable to the free pool */static voidfree_var(NumericVar *var){ digitbuf_free(var->buf); var->buf = NULL; var->digits = NULL; var->sign = NUMERIC_NAN;}/* * zero_var() - * * Set a variable to ZERO. * Note: its dscale is not touched. */static voidzero_var(NumericVar *var){ digitbuf_free(var->buf); var->buf = NULL; var->digits = NULL; var->ndigits = 0; var->weight = 0; /* by convention; doesn't really matter */ var->sign = NUMERIC_POS; /* anything but NAN... */}/* * set_var_from_str() * * Parse a string and put the number into a variable */static voidset_var_from_str(const char *str, NumericVar *dest){ const char *cp = str; bool have_dp = FALSE; int i; unsigned char *decdigits; int sign = NUMERIC_POS; int dweight = -1; int ddigits; int dscale = 0; int weight; int ndigits; int offset; NumericDigit *digits; /* * We first parse the string to extract decimal digits and determine the * correct decimal weight. Then convert to NBASE representation. */ /* skip leading spaces */ while (*cp) { if (!isspace((unsigned char) *cp)) break; cp++; } switch (*cp) { case '+': sign = NUMERIC_POS; cp++; break; case '-': sign = NUMERIC_NEG; cp++; break; } if (*cp == '.') { have_dp = TRUE; cp++; } if (!isdigit((unsigned char) *cp)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2); /* leading padding for digit alignment later */ memset(decdigits, 0, DEC_DIGITS); i = DEC_DIGITS; while (*cp) { if (isdigit((unsigned char) *cp)) { decdigits[i++] = *cp++ - '0'; if (!have_dp) dweight++; else dscale++; } else if (*cp == '.') { if (have_dp) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); have_dp = TRUE; cp++; } else break; } ddigits = i - DEC_DIGITS; /* trailing padding for digit alignment later */ memset(decdigits + i, 0, DEC_DIGITS - 1); /* Handle exponent, if any */ if (*cp == 'e' || *cp == 'E') { long exponent; char *endptr; cp++; exponent = strtol(cp, &endptr, 10); if (endptr == cp) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); cp = endptr; if (exponent > NUMERIC_MAX_PRECISION || exponent < -NUMERIC_MAX_PRECISION) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); dweight += (int) exponent; dscale -= (int) exponent; if (dscale < 0) dscale = 0; } /* Should be nothing left but spaces */ while (*cp) { if (!isspace((unsigned char) *cp)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeric: \"%s\"", str))); cp++; } /* * Okay, convert pure-decimal representation to base NBASE. First we need * to determine the converted weight and ndigits. offset is the number of * decimal zeroes to insert before the first given digit to have a * correctly aligned first NBASE digit. */ if (dweight >= 0) weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1; else weight = -((-dweight - 1) / DEC_DIGITS + 1); offset = (weight + 1) * DEC_DIGITS - (dweight + 1); ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS; alloc_var(dest, ndigits); dest->sign = sign; dest->weight = weight; dest->dscale = dscale; i = DEC_DIGITS - offset; digits = dest->digits; while (ndigits-- > 0) {#if DEC_DIGITS == 4 *digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 + decdigits[i + 2]) * 10 + decdigits[i + 3];#elif DEC_DIGITS == 2 *digits++ = decdigits[i] * 10 + decdigits[i + 1];#elif DEC_DIGITS == 1 *digits++ = decdigits[i];#else#error unsupported NBASE#endif i += DEC_DIGITS; } pfree(decdigits); /* Strip any leading/trailing zeroes, and normalize weight if zero */ strip_var(dest);}/* * set_var_from_num() - * * Convert the packed db format into a variable */static voidset_var_from_num(Numeric num, NumericVar *dest){ int ndigits; ndigits = (num->varlen - NUMERIC_HDRSZ) / sizeof(NumericDigit); alloc_var(dest, ndigits); dest->weight = num->n_weight; dest->sign = NUMERIC_SIGN(num);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -