diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2000-07-17 03:05:41 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2000-07-17 03:05:41 +0000 |
commit | bec98a31c55a4f799b398d01541e68d7c086bb81 (patch) | |
tree | 14924bb5da2bc0a0f9bfac1aa5b32256fd996b9c /src/backend/utils | |
parent | 139f19c30221968e7d3bf64fe303cb41517e4601 (diff) | |
download | postgresql-bec98a31c55a4f799b398d01541e68d7c086bb81.tar.gz postgresql-bec98a31c55a4f799b398d01541e68d7c086bb81.zip |
Revise aggregate functions per earlier discussions in pghackers.
There's now only one transition value and transition function.
NULL handling in aggregates is a lot cleaner. Also, use Numeric
accumulators instead of integer accumulators for sum/avg on integer
datatypes --- this avoids overflow at the cost of being a little slower.
Implement VARIANCE() and STDDEV() aggregates in the standard backend.
Also, enable new LIKE selectivity estimators by default. Unrelated
change, but as long as I had to force initdb anyway...
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 404 | ||||
-rw-r--r-- | src/backend/utils/adt/float.c | 226 | ||||
-rw-r--r-- | src/backend/utils/adt/int.c | 10 | ||||
-rw-r--r-- | src/backend/utils/adt/numeric.c | 596 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 92 |
5 files changed, 1005 insertions, 323 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 3fc00613042..907082a7268 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.60 2000/07/03 23:09:50 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.61 2000/07/17 03:05:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,6 @@ #include "catalog/catalog.h" #include "catalog/pg_type.h" -#include "fmgr.h" #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" #include "storage/fd.h" @@ -29,7 +28,8 @@ #define ASSGN "=" -/* An array has the following internal structure: +/* + * An array has the following internal structure: * <nbytes> - total number of bytes * <ndim> - number of dimensions of the array * <flags> - bit mask of flags @@ -38,20 +38,18 @@ * <actual data> - whatever is the stored data */ -/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/ static int _ArrayCount(char *str, int *dim, int typdelim); -static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim, +static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim, FmgrInfo *inputproc, Oid typelem, int32 typmod, char typdelim, int typlen, bool typbyval, char typalign, int *nbytes); - #ifdef LOARRAY static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag, int ndim, int *dim, int baseSize); - #endif -static void _CopyArrayEls(char **values, char *p, int nitems, int typlen, - char typalign, bool typbyval); +static void CopyArrayEls(char *p, Datum *values, int nitems, + bool typbyval, int typlen, char typalign, + bool freedata); static void system_cache_lookup(Oid element_type, bool input, int *typlen, bool *typbyval, char *typdelim, Oid *typelem, Oid *proc, char *typalign); @@ -101,7 +99,7 @@ array_in(PG_FUNCTION_ARGS) int i, nitems; int32 nbytes; - char *dataPtr; + Datum *dataPtr; ArrayType *retval; int ndim, dim[MAXDIM], @@ -187,32 +185,29 @@ array_in(PG_FUNCTION_ARGS) retval = (ArrayType *) palloc(sizeof(ArrayType)); MemSet(retval, 0, sizeof(ArrayType)); *(int32 *) retval = sizeof(ArrayType); - PG_RETURN_POINTER(retval); + PG_RETURN_ARRAYTYPE_P(retval); } if (*p == '{') { /* array not a large object */ - dataPtr = (char *) _ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem, - typmod, typdelim, typlen, typbyval, typalign, - &nbytes); + dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem, + typmod, typdelim, typlen, typbyval, typalign, + &nbytes); nbytes += ARR_OVERHEAD(ndim); retval = (ArrayType *) palloc(nbytes); MemSet(retval, 0, nbytes); - memmove(retval, (char *) &nbytes, sizeof(int)); - memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int)); + retval->size = nbytes; + retval->ndim = ndim; SET_LO_FLAG(false, retval); - memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int)); - memmove((char *) ARR_LBOUND(retval), (char *) lBound, - ndim * sizeof(int)); - - /* - * dataPtr is an array of arbitraystuff even though its type is - * char* cast to char** to pass to _CopyArrayEls for now - jolly - */ - _CopyArrayEls((char **) dataPtr, - ARR_DATA_PTR(retval), nitems, - typlen, typalign, typbyval); + memcpy((char *) ARR_DIMS(retval), (char *) dim, + ndim * sizeof(int)); + memcpy((char *) ARR_LBOUND(retval), (char *) lBound, + ndim * sizeof(int)); + + CopyArrayEls(ARR_DATA_PTR(retval), dataPtr, nitems, + typbyval, typlen, typalign, true); + pfree(dataPtr); } else { @@ -226,8 +221,8 @@ array_in(PG_FUNCTION_ARGS) nbytes = bytes + ARR_OVERHEAD(ndim); retval = (ArrayType *) palloc(nbytes); MemSet(retval, 0, nbytes); - memmove(retval, (char *) &nbytes, sizeof(int)); - memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int)); + retval->size = nbytes; + retval->ndim = ndim; SET_LO_FLAG(true, retval); SET_CHUNK_FLAG(chunked, retval); memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int)); @@ -238,7 +233,7 @@ array_in(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } pfree(string_save); - PG_RETURN_POINTER(retval); + PG_RETURN_ARRAYTYPE_P(retval); } /*----------------------------------------------------------------------------- @@ -331,50 +326,51 @@ _ArrayCount(char *str, int *dim, int typdelim) } /*--------------------------------------------------------------------------- - * _ReadArrayStr : - * parses the array string pointed by "arrayStr" and converts it in the + * ReadArrayStr : + * parses the array string pointed by "arrayStr" and converts it to * internal format. The external format expected is like C array * declaration. Unspecified elements are initialized to zero for fixed length * base types and to empty varlena structures for variable length base * types. * result : - * returns the internal representation of the array elements - * nbytes is set to the size of the array in its internal representation. + * returns a palloc'd array of Datum representations of the array elements. + * If element type is pass-by-ref, the Datums point to palloc'd values. + * *nbytes is set to the amount of data space needed for the array, + * including alignment padding but not including array header overhead. *--------------------------------------------------------------------------- */ -static char * -_ReadArrayStr(char *arrayStr, - int nitems, - int ndim, - int *dim, - FmgrInfo *inputproc, /* function used for the - * conversion */ - Oid typelem, - int32 typmod, - char typdelim, - int typlen, - bool typbyval, - char typalign, - int *nbytes) +static Datum * +ReadArrayStr(char *arrayStr, + int nitems, + int ndim, + int *dim, + FmgrInfo *inputproc, + Oid typelem, + int32 typmod, + char typdelim, + int typlen, + bool typbyval, + char typalign, + int *nbytes) { int i, nest_level = 0; + Datum *values; char *p, *q, - *r, - **values; + *r; bool scanning_string = false; int indx[MAXDIM], prod[MAXDIM]; bool eoArray = false; mda_get_prod(ndim, dim, prod); - for (i = 0; i < ndim; indx[i++] = 0); - /* read array enclosed within {} */ - values = (char **) palloc(nitems * sizeof(char *)); - MemSet(values, 0, nitems * sizeof(char *)); + values = (Datum *) palloc(nitems * sizeof(Datum)); + MemSet(values, 0, nitems * sizeof(Datum)); + MemSet(indx, 0, sizeof(indx)); q = p = arrayStr; + /* read array enclosed within {} */ while (!eoArray) { bool done = false; @@ -442,53 +438,56 @@ _ReadArrayStr(char *arrayStr, *q = '\0'; if (i >= nitems) elog(ERROR, "array_in: illformed array constant"); - values[i] = (char *) FunctionCall3(inputproc, - CStringGetDatum(p), - ObjectIdGetDatum(typelem), - Int32GetDatum(typmod)); + values[i] = FunctionCall3(inputproc, + CStringGetDatum(p), + ObjectIdGetDatum(typelem), + Int32GetDatum(typmod)); p = ++q; + /* + * if not at the end of the array skip white space + */ if (!eoArray) - - /* - * if not at the end of the array skip white space - */ while (isspace((int) *q)) { p++; q++; } } + /* + * Initialize any unset items and compute total data space needed + */ if (typlen > 0) { *nbytes = nitems * typlen; if (!typbyval) for (i = 0; i < nitems; i++) - if (!values[i]) + if (values[i] == (Datum) 0) { - values[i] = palloc(typlen); - MemSet(values[i], 0, typlen); + values[i] = PointerGetDatum(palloc(typlen)); + MemSet(DatumGetPointer(values[i]), 0, typlen); } } else { - for (i = 0, *nbytes = 0; i < nitems; i++) + *nbytes = 0; + for (i = 0; i < nitems; i++) { - if (values[i]) + if (values[i] != (Datum) 0) { if (typalign == 'd') - *nbytes += MAXALIGN(*(int32 *) values[i]); + *nbytes += MAXALIGN(VARSIZE(DatumGetPointer(values[i]))); else - *nbytes += INTALIGN(*(int32 *) values[i]); + *nbytes += INTALIGN(VARSIZE(DatumGetPointer(values[i]))); } else { *nbytes += sizeof(int32); - values[i] = palloc(sizeof(int32)); - *(int32 *) values[i] = sizeof(int32); + values[i] = PointerGetDatum(palloc(sizeof(int32))); + VARATT_SIZEP(DatumGetPointer(values[i])) = sizeof(int32); } } } - return (char *) values; + return values; } @@ -565,26 +564,39 @@ _ReadLOArray(char *str, #endif +/*---------- + * Copy data into an array object from a temporary array of Datums. + * + * p: pointer to start of array data area + * values: array of Datums to be copied + * nitems: number of Datums to be copied + * typbyval, typlen, typalign: info about element datatype + * freedata: if TRUE and element type is pass-by-ref, pfree data values + * referenced by Datums after copying them. + *---------- + */ static void -_CopyArrayEls(char **values, - char *p, - int nitems, - int typlen, - char typalign, - bool typbyval) +CopyArrayEls(char *p, + Datum *values, + int nitems, + bool typbyval, + int typlen, + char typalign, + bool freedata) { int i; + int inc; + + if (typbyval) + freedata = false; for (i = 0; i < nitems; i++) { - int inc; - - inc = ArrayCastAndSet((Datum) values[i], typbyval, typlen, p); + inc = ArrayCastAndSet(values[i], typbyval, typlen, p); p += inc; - if (!typbyval) - pfree(values[i]); + if (freedata) + pfree(DatumGetPointer(values[i])); } - pfree(values); } /*------------------------------------------------------------------------- @@ -596,7 +608,7 @@ _CopyArrayEls(char **values, Datum array_out(PG_FUNCTION_ARGS) { - ArrayType *v = (ArrayType *) PG_GETARG_VARLENA_P(0); + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); Oid element_type = PG_GETARG_OID(1); int typlen; bool typbyval; @@ -786,7 +798,7 @@ array_out(PG_FUNCTION_ARGS) Datum array_dims(PG_FUNCTION_ARGS) { - ArrayType *v = (ArrayType *) PG_GETARG_VARLENA_P(0); + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); text *result; char *p; int nbytes, @@ -821,8 +833,8 @@ array_dims(PG_FUNCTION_ARGS) /*--------------------------------------------------------------------------- * array_ref : * This routine takes an array pointer and an index array and returns - * a pointer to the referred element if element is passed by - * reference otherwise returns the value of the referred element. + * the referenced item as a Datum. Note that for a pass-by-reference + * datatype, the returned Datum is a pointer into the array object. *--------------------------------------------------------------------------- */ Datum @@ -905,7 +917,7 @@ array_ref(ArrayType *array, { /* not by value */ char *tempdata = palloc(elmlen); - memmove(tempdata, DatumGetPointer(result), elmlen); + memcpy(tempdata, DatumGetPointer(result), elmlen); result = PointerGetDatum(tempdata); } pfree(v); @@ -1003,14 +1015,15 @@ array_clip(ArrayType *array, #endif bytes = strlen(newname) + 1 + ARR_OVERHEAD(nSubscripts); newArr = (ArrayType *) palloc(bytes); - memmove(newArr, array, sizeof(ArrayType)); - memmove(newArr, &bytes, sizeof(int)); - memmove(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); - memmove(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); + newArr->size = bytes; + newArr->ndim = array->ndim; + newArr->flags = array->flags; + memcpy(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); + memcpy(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); strcpy(ARR_DATA_PTR(newArr), newname); rsize = compute_size(lowerIndx, upperIndx, nSubscripts, elmlen); - if (rsize < MAX_BUFF_SIZE) + if (rsize < BLCKSZ) { char *buff; @@ -1072,10 +1085,11 @@ array_clip(ArrayType *array, bytes += ARR_OVERHEAD(nSubscripts); } newArr = (ArrayType *) palloc(bytes); - memmove(newArr, array, sizeof(ArrayType)); - memmove(newArr, &bytes, sizeof(int)); - memmove(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); - memmove(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); + newArr->size = bytes; + newArr->ndim = array->ndim; + newArr->flags = array->flags; + memcpy(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); + memcpy(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); _ArrayRange(lowerIndx, upperIndx, elmlen, ARR_DATA_PTR(newArr), array, 1); return newArr; } @@ -1322,7 +1336,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) { ArrayType *v; ArrayType *result; - char **values; + Datum *values; char *elt; int *dim; int ndim; @@ -1338,14 +1352,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) Oid proc; char typalign; char *s; - char *p; /* Get input array */ if (fcinfo->nargs < 1) elog(ERROR, "array_map: invalid nargs: %d", fcinfo->nargs); if (PG_ARGISNULL(0)) elog(ERROR, "array_map: null input array"); - v = (ArrayType *) PG_GETARG_VARLENA_P(0); + v = PG_GETARG_ARRAYTYPE_P(0); /* Large objects not yet supported */ if (ARR_IS_LO(v) == true) @@ -1357,7 +1370,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) /* Check for empty array */ if (nitems <= 0) - PG_RETURN_POINTER(v); + PG_RETURN_ARRAYTYPE_P(v); /* Lookup source and result types. Unneeded variables are reused. */ system_cache_lookup(inpType, false, &inp_typlen, &inp_typbyval, @@ -1366,8 +1379,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) &typdelim, &typelem, &proc, &typalign); /* Allocate temporary array for new values */ - values = (char **) palloc(nitems * sizeof(char *)); - MemSet(values, 0, nitems * sizeof(char *)); + values = (Datum *) palloc(nitems * sizeof(Datum)); + MemSet(values, 0, nitems * sizeof(Datum)); /* Loop over source data */ s = (char *) ARR_DATA_PTR(v); @@ -1411,30 +1424,16 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) fcinfo->arg[0] = (Datum) elt; fcinfo->argnull[0] = false; fcinfo->isnull = false; - p = (char *) FunctionCallInvoke(fcinfo); + values[i] = FunctionCallInvoke(fcinfo); if (fcinfo->isnull) elog(ERROR, "array_map: cannot handle NULL in array"); - /* Update values and total result size */ + /* Update total result size */ if (typbyval) - { - values[i] = p; nbytes += typlen; - } else - { - int len; - - len = ((typlen > 0) ? typlen : INTALIGN(*(int32 *) p)); - /* Needed because _CopyArrayEls tries to pfree items */ - if (p == elt) - { - p = (char *) palloc(len); - memcpy(p, elt, len); - } - values[i] = p; - nbytes += len; - } + nbytes += ((typlen > 0) ? typlen : + INTALIGN(VARSIZE(DatumGetPointer(values[i])))); } /* Allocate and initialize the result array */ @@ -1442,18 +1441,130 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) result = (ArrayType *) palloc(nbytes); MemSet(result, 0, nbytes); - memcpy((char *) result, (char *) &nbytes, sizeof(int)); - memcpy((char *) ARR_NDIM_PTR(result), (char *) &ndim, sizeof(int)); - memcpy((char *) ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int)); + result->size = nbytes; + result->ndim = ndim; + memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int)); - /* Copy new values into the result array. values is pfreed. */ - _CopyArrayEls((char **) values, - ARR_DATA_PTR(result), nitems, - typlen, typalign, typbyval); + /* Note: do not risk trying to pfree the results of the called function */ + CopyArrayEls(ARR_DATA_PTR(result), values, nitems, + typbyval, typlen, typalign, false); + pfree(values); - PG_RETURN_POINTER(result); + PG_RETURN_ARRAYTYPE_P(result); } +/*---------- + * construct_array --- simple method for constructing an array object + * + * elems: array of Datum items to become the array contents + * nelems: number of items + * elmbyval, elmlen, elmalign: info for the datatype of the items + * + * A palloc'd 1-D array object is constructed and returned. Note that + * elem values will be copied into the object even if pass-by-ref type. + * NULL element values are not supported. + *---------- + */ +ArrayType * +construct_array(Datum *elems, int nelems, + bool elmbyval, int elmlen, char elmalign) +{ + ArrayType *result; + int nbytes; + int i; + + if (elmlen > 0) + { + /* XXX what about alignment? */ + nbytes = elmlen * nelems; + } + else + { + /* varlena type */ + nbytes = 0; + for (i = 0; i < nelems; i++) + nbytes += INTALIGN(VARSIZE(DatumGetPointer(elems[i]))); + } + + /* Allocate and initialize 1-D result array */ + nbytes += ARR_OVERHEAD(1); + result = (ArrayType *) palloc(nbytes); + + result->size = nbytes; + result->ndim = 1; + result->flags = 0; + ARR_DIMS(result)[0] = nelems; + ARR_LBOUND(result)[0] = 1; + + CopyArrayEls(ARR_DATA_PTR(result), elems, nelems, + elmbyval, elmlen, elmalign, false); + + return result; +} + +/*---------- + * deconstruct_array --- simple method for extracting data from an array + * + * array: array object to examine (must not be NULL) + * elmbyval, elmlen, elmalign: info for the datatype of the items + * elemsp: return value, set to point to palloc'd array of Datum values + * nelemsp: return value, set to number of extracted values + * + * If array elements are pass-by-ref data type, the returned Datums will + * be pointers into the array object. + *---------- + */ +void +deconstruct_array(ArrayType *array, + bool elmbyval, int elmlen, char elmalign, + Datum **elemsp, int *nelemsp) +{ + Datum *elems; + int nelems; + char *p; + int i; + + nelems = getNitems(ARR_NDIM(array), ARR_DIMS(array)); + if (nelems <= 0) + { + *elemsp = NULL; + *nelemsp = 0; + return; + } + *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum)); + *nelemsp = nelems; + + p = ARR_DATA_PTR(array); + for (i = 0; i < nelems; i++) + { + if (elmbyval) + { + switch (elmlen) + { + case 1: + elems[i] = CharGetDatum(*p); + break; + case 2: + elems[i] = Int16GetDatum(*(int16 *) p); + break; + case 4: + elems[i] = Int32GetDatum(*(int32 *) p); + break; + } + p += elmlen; + } + else + { + elems[i] = PointerGetDatum(p); + if (elmlen > 0) + p += elmlen; + else + p += INTALIGN(VARSIZE(p)); + } + } +} + + /*----------------------------------------------------------------------------- * array_eq : * compares two arrays for equality @@ -1464,8 +1575,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) Datum array_eq(PG_FUNCTION_ARGS) { - ArrayType *array1 = (ArrayType *) PG_GETARG_VARLENA_P(0); - ArrayType *array2 = (ArrayType *) PG_GETARG_VARLENA_P(1); + ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); + ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); if (*(int32 *) array1 != *(int32 *) array2) PG_RETURN_BOOL(false); @@ -1493,14 +1604,11 @@ system_cache_lookup(Oid element_type, typeTuple = SearchSysCacheTuple(TYPEOID, ObjectIdGetDatum(element_type), 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - { - elog(ERROR, "array_out: Cache lookup failed for type %u\n", + elog(ERROR, "array_out: Cache lookup failed for type %u", element_type); - return; - } typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + *typlen = typeStruct->typlen; *typbyval = typeStruct->typbyval; *typdelim = typeStruct->typdelim; @@ -1536,7 +1644,11 @@ _ArrayCast(char *value, bool byval, int len) return 0; } - +/* + * Copy datum to *dest and return total space used (including align padding) + * + * XXX this routine needs to be told typalign too! + */ static int ArrayCastAndSet(Datum src, bool typbyval, @@ -1560,16 +1672,26 @@ ArrayCastAndSet(Datum src, case 4: *(int32 *) dest = DatumGetInt32(src); break; + default: + elog(ERROR, "ArrayCastAndSet: unexpected typlen"); + break; } + /* For by-val types, assume no alignment padding is needed */ + inc = typlen; } else + { memmove(dest, DatumGetPointer(src), typlen); - inc = typlen; + /* XXX WRONG: need to consider type's alignment requirement */ + inc = typlen; + } } else { - memmove(dest, DatumGetPointer(src), *(int32 *) DatumGetPointer(src)); - inc = (INTALIGN(*(int32 *) DatumGetPointer(src))); + /* varlena type */ + memmove(dest, DatumGetPointer(src), VARSIZE(DatumGetPointer(src))); + /* XXX WRONG: should use MAXALIGN or type's alignment requirement */ + inc = INTALIGN(VARSIZE(DatumGetPointer(src))); } return inc; } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 0b6a0db2eaa..bfa439f4156 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.64 2000/07/12 22:59:08 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.65 2000/07/17 03:05:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ * Basic float4 ops: * float4in, float4out, float4abs, float4um * Basic float8 ops: - * float8in, float8inAd, float8out, float8outAd, float8abs, float8um + * float8in, float8out, float8abs, float8um * Arithmetic operators: * float4pl, float4mi, float4mul, float4div * float8pl, float8mi, float8mul, float8div @@ -64,6 +64,7 @@ #endif #include "fmgr.h" +#include "utils/array.h" #include "utils/builtins.h" static void CheckFloat8Val(double val); @@ -90,7 +91,6 @@ static void CheckFloat8Val(double val); #ifndef atof extern double atof(const char *p); - #endif #ifndef HAVE_CBRT @@ -100,9 +100,8 @@ static double cbrt(double x); #else #if !defined(nextstep) extern double cbrt(double x); - -#endif #endif +#endif /* HAVE_CBRT */ #ifndef HAVE_RINT #define rint my_rint @@ -110,10 +109,9 @@ static double rint(double x); #else extern double rint(double x); +#endif /* HAVE_RINT */ -#endif - -#endif +#endif /* NeXT check */ /* ========== USER I/O ROUTINES ========== */ @@ -453,7 +451,6 @@ float8smaller(float64 arg1, float64 arg2) * float4mi - returns a pointer to arg1 - arg2 * float4mul - returns a pointer to arg1 * arg2 * float4div - returns a pointer to arg1 / arg2 - * float4inc - returns a poniter to arg1 + 1.0 */ float32 float4pl(float32 arg1, float32 arg2) @@ -527,29 +524,11 @@ float4div(float32 arg1, float32 arg2) return result; } -float32 -float4inc(float32 arg1) -{ - float32 result; - double val; - - if (!arg1) - return (float32) NULL; - - val = *arg1 + (float32data) 1.0; - - CheckFloat4Val(val); - result = (float32) palloc(sizeof(float32data)); - *result = val; - return result; -} - /* * float8pl - returns a pointer to arg1 + arg2 * float8mi - returns a pointer to arg1 - arg2 * float8mul - returns a pointer to arg1 * arg2 * float8div - returns a pointer to arg1 / arg2 - * float8inc - returns a pointer to arg1 + 1.0 */ float64 float8pl(float64 arg1, float64 arg2) @@ -622,22 +601,6 @@ float8div(float64 arg1, float64 arg2) return result; } -float64 -float8inc(float64 arg1) -{ - float64 result; - double val; - - if (!arg1) - return (float64) NULL; - - val = *arg1 + (float64data) 1.0; - CheckFloat8Val(val); - result = (float64) palloc(sizeof(float64data)); - *result = val; - return result; -} - /* * ==================== @@ -1572,10 +1535,181 @@ setseed(float64 seed) } /* setseed() */ + /* - * ==================== - * ARITHMETIC OPERATORS - * ==================== + * ========================= + * FLOAT AGGREGATE OPERATORS + * ========================= + * + * float8_accum - accumulate for AVG(), STDDEV(), etc + * float4_accum - same, but input data is float4 + * float8_avg - produce final result for float AVG() + * float8_variance - produce final result for float VARIANCE() + * float8_stddev - produce final result for float STDDEV() + * + * The transition datatype for all these aggregates is a 3-element array + * of float8, holding the values N, sum(X), sum(X*X) in that order. + * + * Note that we represent N as a float to avoid having to build a special + * datatype. Given a reasonable floating-point implementation, there should + * be no accuracy loss unless N exceeds 2 ^ 52 or so (by which time the + * user will have doubtless lost interest anyway...) + */ + +static float8 * +check_float8_array(ArrayType *transarray, const char *caller) +{ + /* + * We expect the input to be a 3-element float array; verify that. + * We don't need to use deconstruct_array() since the array data + * is just going to look like a C array of 3 float8 values. + */ + if (ARR_SIZE(transarray) != (ARR_OVERHEAD(1) + 3 * sizeof(float8)) || + ARR_NDIM(transarray) != 1 || + ARR_DIMS(transarray)[0] != 3) + elog(ERROR, "%s: expected 3-element float8 array", caller); + return (float8 *) ARR_DATA_PTR(transarray); +} + +Datum +float8_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 newval = PG_GETARG_FLOAT8(1); + float8 *transvalues; + float8 N, + sumX, + sumX2; + Datum transdatums[3]; + ArrayType *result; + + transvalues = check_float8_array(transarray, "float8_accum"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + N += 1.0; + sumX += newval; + sumX2 += newval * newval; + + transdatums[0] = Float8GetDatumFast(N); + transdatums[1] = Float8GetDatumFast(sumX); + transdatums[2] = Float8GetDatumFast(sumX2); + + result = construct_array(transdatums, 3, + false /* float8 byval */, sizeof(float8), 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum +float4_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float4 newval4 = PG_GETARG_FLOAT4(1); + float8 *transvalues; + float8 N, + sumX, + sumX2, + newval; + Datum transdatums[3]; + ArrayType *result; + + transvalues = check_float8_array(transarray, "float4_accum"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + /* Do arithmetic in float8 for best accuracy */ + newval = newval4; + + N += 1.0; + sumX += newval; + sumX2 += newval * newval; + + transdatums[0] = Float8GetDatumFast(N); + transdatums[1] = Float8GetDatumFast(sumX); + transdatums[2] = Float8GetDatumFast(sumX2); + + result = construct_array(transdatums, 3, + false /* float8 byval */, sizeof(float8), 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum +float8_avg(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 *transvalues; + float8 N, + sumX; + + transvalues = check_float8_array(transarray, "float8_avg"); + N = transvalues[0]; + sumX = transvalues[1]; + /* ignore sumX2 */ + + /* SQL92 defines AVG of no values to be NULL */ + if (N == 0.0) + PG_RETURN_NULL(); + + PG_RETURN_FLOAT8(sumX / N); +} + +Datum +float8_variance(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 *transvalues; + float8 N, + sumX, + sumX2; + + transvalues = check_float8_array(transarray, "float8_variance"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + /* We define VARIANCE of no values to be NULL, of 1 value to be 0 */ + if (N == 0.0) + PG_RETURN_NULL(); + + if (N <= 1.0) + PG_RETURN_FLOAT8(0.0); + + PG_RETURN_FLOAT8((N * sumX2 - sumX * sumX) / (N * (N - 1.0))); +} + +Datum +float8_stddev(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 *transvalues; + float8 N, + sumX, + sumX2; + + transvalues = check_float8_array(transarray, "float8_stddev"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + /* We define STDDEV of no values to be NULL, of 1 value to be 0 */ + if (N == 0.0) + PG_RETURN_NULL(); + + if (N <= 1.0) + PG_RETURN_FLOAT8(0.0); + + PG_RETURN_FLOAT8(sqrt((N * sumX2 - sumX * sumX) / (N * (N - 1.0)))); +} + + +/* + * ==================================== + * MIXED-PRECISION ARITHMETIC OPERATORS + * ==================================== */ /* diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index bf7758c1865..7133142c0b6 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.40 2000/07/12 22:59:08 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.41 2000/07/17 03:05:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -651,14 +651,6 @@ int2div(PG_FUNCTION_ARGS) } Datum -int2inc(PG_FUNCTION_ARGS) -{ - int16 arg = PG_GETARG_INT16(0); - - PG_RETURN_INT16(arg + 1); -} - -Datum int24pl(PG_FUNCTION_ARGS) { int16 arg1 = PG_GETARG_INT16(0); diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 5748986bbfe..437bb69b832 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -5,7 +5,7 @@ * * 1998 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.31 2000/06/15 03:32:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.32 2000/07/17 03:05:18 tgl Exp $ * * ---------- */ @@ -18,6 +18,7 @@ #include <errno.h> #include <sys/types.h> +#include "utils/array.h" #include "utils/builtins.h" #include "utils/int8.h" #include "utils/numeric.h" @@ -1231,49 +1232,6 @@ numeric_inc(Numeric num) /* ---------- - * numeric_dec() - - * - * Decrement a number by one - * ---------- - */ -Numeric -numeric_dec(Numeric num) -{ - NumericVar arg; - Numeric res; - - /* ---------- - * Handle NULL - * ---------- - */ - if (num == NULL) - return NULL; - - /* ---------- - * Handle NaN - * ---------- - */ - if (NUMERIC_IS_NAN(num)) - return make_result(&const_nan); - - /* ---------- - * Compute the result and return it - * ---------- - */ - init_var(&arg); - - set_var_from_num(num, &arg); - - sub_var(&arg, &const_one, &arg); - res = make_result(&arg); - - free_var(&arg); - - return res; -} - - -/* ---------- * numeric_smaller() - * * Return the smaller of two numbers @@ -1733,24 +1691,24 @@ numeric_int4(Numeric num) } -Numeric -int8_numeric(int64 *val) +Datum +int8_numeric(PG_FUNCTION_ARGS) { + Datum val = PG_GETARG_DATUM(0); Numeric res; NumericVar result; char *tmp; init_var(&result); - tmp = DatumGetCString(DirectFunctionCall1(int8out, - PointerGetDatum(val))); + tmp = DatumGetCString(DirectFunctionCall1(int8out, val)); set_var_from_str(tmp, &result); res = make_result(&result); free_var(&result); pfree(tmp); - return res; + PG_RETURN_NUMERIC(res); } @@ -1941,6 +1899,369 @@ numeric_float4(Numeric num) /* ---------------------------------------------------------------------- * + * 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; + Numeric N, + sumX, + sumX2; + ArrayType *result; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "do_numeric_accum: expected 3-element numeric array"); + N = DatumGetNumeric(transdatums[0]); + sumX = DatumGetNumeric(transdatums[1]); + sumX2 = DatumGetNumeric(transdatums[2]); + + N = numeric_inc(N); + sumX = numeric_add(sumX, newval); + sumX2 = numeric_add(sumX2, numeric_mul(newval, newval)); + + transdatums[0] = NumericGetDatum(N); + transdatums[1] = NumericGetDatum(sumX); + transdatums[2] = NumericGetDatum(sumX2); + + result = construct_array(transdatums, 3, + false, -1, 'i'); + + return result; +} + +Datum +numeric_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. + */ + +Datum +int2_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)); +} + +Datum +int4_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)); +} + +Datum +int8_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)); +} + +Datum +numeric_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, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "numeric_avg: 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_NUMERIC(numeric_div(sumX, N)); +} + +Datum +numeric_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; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "numeric_variance: 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)); + + /* We define VARIANCE of no values to be NULL, of 1 value to be 0 */ + /* N is zero iff no digits (cf. numeric_uminus) */ + if (N->varlen == NUMERIC_HDRSZ) + PG_RETURN_NULL(); + + init_var(&vN); + set_var_from_num(N, &vN); + + init_var(&vNminus1); + sub_var(&vN, &const_one, &vNminus1); + + if (cmp_var(&vNminus1, &const_zero) <= 0) + { + free_var(&vN); + free_var(&vNminus1); + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + init_var(&vsumX); + set_var_from_num(sumX, &vsumX); + init_var(&vsumX2); + set_var_from_num(sumX2, &vsumX2); + + mul_var(&vsumX, &vsumX, &vsumX); /* now vsumX contains sumX * sumX */ + mul_var(&vN, &vsumX2, &vsumX2); /* now vsumX2 contains N * sumX2 */ + sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ + mul_var(&vN, &vNminus1, &vNminus1); /* N * (N - 1) */ + div_var(&vsumX2, &vNminus1, &vsumX); /* variance */ + + res = make_result(&vsumX); + + free_var(&vN); + free_var(&vNminus1); + free_var(&vsumX); + free_var(&vsumX2); + + PG_RETURN_NUMERIC(res); +} + +Datum +numeric_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; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "numeric_stddev: 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)); + + /* We define STDDEV of no values to be NULL, of 1 value to be 0 */ + /* N is zero iff no digits (cf. numeric_uminus) */ + if (N->varlen == NUMERIC_HDRSZ) + PG_RETURN_NULL(); + + init_var(&vN); + set_var_from_num(N, &vN); + + init_var(&vNminus1); + sub_var(&vN, &const_one, &vNminus1); + + if (cmp_var(&vNminus1, &const_zero) <= 0) + { + free_var(&vN); + free_var(&vNminus1); + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + init_var(&vsumX); + set_var_from_num(sumX, &vsumX); + init_var(&vsumX2); + set_var_from_num(sumX2, &vsumX2); + + mul_var(&vsumX, &vsumX, &vsumX); /* now vsumX contains sumX * sumX */ + mul_var(&vN, &vsumX2, &vsumX2); /* now vsumX2 contains N * sumX2 */ + sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ + mul_var(&vN, &vNminus1, &vNminus1); /* N * (N - 1) */ + div_var(&vsumX2, &vNminus1, &vsumX); /* variance */ + sqrt_var(&vsumX, &vsumX); /* 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. + * + * We use a Numeric accumulator to avoid overflow. 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. + */ + +Datum +int2_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum, + 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 = DatumGetNumeric(DirectFunctionCall1(int2_numeric, + PG_GETARG_DATUM(1))); + PG_RETURN_NUMERIC(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 = DatumGetNumeric(DirectFunctionCall1(int2_numeric, + PG_GETARG_DATUM(1))); + + PG_RETURN_NUMERIC(numeric_add(oldsum, newval)); +} + +Datum +int4_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum, + 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 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, + PG_GETARG_DATUM(1))); + PG_RETURN_NUMERIC(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 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, + PG_GETARG_DATUM(1))); + + PG_RETURN_NUMERIC(numeric_add(oldsum, newval)); +} + +Datum +int8_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum, + 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 = DatumGetNumeric(DirectFunctionCall1(int8_numeric, + PG_GETARG_DATUM(1))); + PG_RETURN_NUMERIC(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 = DatumGetNumeric(DirectFunctionCall1(int8_numeric, + PG_GETARG_DATUM(1))); + + PG_RETURN_NUMERIC(numeric_add(oldsum, newval)); +} + + +/* ---------------------------------------------------------------------- + * * Local functions follow * * ---------------------------------------------------------------------- @@ -2574,30 +2895,33 @@ add_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = +(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = +(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_POS; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = -(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = -(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_NEG; break; @@ -2615,30 +2939,33 @@ add_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = -(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = -(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_NEG; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = +(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = +(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_POS; break; @@ -2693,30 +3020,33 @@ sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = +(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = +(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_POS; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = -(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = -(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_NEG; break; @@ -2734,30 +3064,33 @@ sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = -(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = -(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_NEG; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = +(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = +(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_POS; break; @@ -2817,7 +3150,7 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result) for (i2 = var2->ndigits - 1; i2 >= 0; i2--) { - sum = sum + res_digits[i] + var1->digits[i1] * var2->digits[i2]; + sum += res_digits[i] + var1->digits[i1] * var2->digits[i2]; res_digits[i--] = sum % 10; sum /= 10; } @@ -3067,7 +3400,6 @@ div_var(NumericVar *var1, NumericVar *var2, NumericVar *result) /* * Tidy up - * */ digitbuf_free(dividend.buf); for (i = 1; i < 10; i++) @@ -3552,6 +3884,11 @@ add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) i1, i2; int carry = 0; + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; res_weight = MAX(var1->weight, var2->weight) + 1; res_rscale = MAX(var1->rscale, var2->rscale); @@ -3569,15 +3906,25 @@ add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) { i1--; i2--; - if (i1 >= 0 && i1 < var1->ndigits) - carry += var1->digits[i1]; - if (i2 >= 0 && i2 < var2->ndigits) - carry += var2->digits[i2]; + if (i1 >= 0 && i1 < var1ndigits) + carry += var1digits[i1]; + if (i2 >= 0 && i2 < var2ndigits) + carry += var2digits[i2]; - res_digits[i] = carry % 10; - carry /= 10; + if (carry >= 10) + { + res_digits[i] = carry - 10; + carry = 1; + } + else + { + res_digits[i] = carry; + carry = 0; + } } + Assert(carry == 0); /* else we failed to allow for carry out */ + while (res_ndigits > 0 && *res_digits == 0) { res_digits++; @@ -3623,6 +3970,11 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) i1, i2; int borrow = 0; + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; res_weight = var1->weight; res_rscale = MAX(var1->rscale, var2->rscale); @@ -3640,10 +3992,10 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) { i1--; i2--; - if (i1 >= 0 && i1 < var1->ndigits) - borrow += var1->digits[i1]; - if (i2 >= 0 && i2 < var2->ndigits) - borrow -= var2->digits[i2]; + if (i1 >= 0 && i1 < var1ndigits) + borrow += var1digits[i1]; + if (i2 >= 0 && i2 < var2ndigits) + borrow -= var2digits[i2]; if (borrow < 0) { @@ -3657,6 +4009,8 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) } } + Assert(borrow == 0); /* else caller gave us var1 < var2 */ + while (res_ndigits > 0 && *res_digits == 0) { res_digits++; diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index b4736dd6ae8..0730d561473 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.33 2000/07/12 22:59:09 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.34 2000/07/17 03:05:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,7 @@ #include "access/hash.h" #include "access/xact.h" #include "miscadmin.h" +#include "utils/array.h" #include "utils/builtins.h" @@ -882,10 +883,6 @@ overlaps_timestamp(PG_FUNCTION_ARGS) /*---------------------------------------------------------- * "Arithmetic" operators on date/times. - * timestamp_foo returns foo as an object (pointer) that - * can be passed between languages. - * timestamp_xx is an internal routine which returns the - * actual value. *---------------------------------------------------------*/ Datum @@ -1150,7 +1147,6 @@ interval_larger(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } - Datum interval_pl(PG_FUNCTION_ARGS) { @@ -1232,6 +1228,90 @@ interval_div(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } +/* + * interval_accum and interval_avg implement the AVG(interval) aggregate. + * + * The transition datatype for this aggregate is a 2-element array of + * intervals, where the first is the running sum and the second contains + * the number of values so far in its 'time' field. This is a bit ugly + * but it beats inventing a specialized datatype for the purpose. + */ + +Datum +interval_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Interval *newval = PG_GETARG_INTERVAL_P(1); + Datum *transdatums; + int ndatums; + Interval sumX, + N; + Interval *newsum; + ArrayType *result; + + /* We assume the input is array of interval */ + deconstruct_array(transarray, + false, 12, 'd', + &transdatums, &ndatums); + if (ndatums != 2) + elog(ERROR, "interval_accum: expected 2-element interval array"); + /* + * XXX memcpy, instead of just extracting a pointer, to work around + * buggy array code: it won't ensure proper alignment of Interval + * objects on machines where double requires 8-byte alignment. + * That should be fixed, but in the meantime... + */ + memcpy(&sumX, DatumGetIntervalP(transdatums[0]), sizeof(Interval)); + memcpy(&N, DatumGetIntervalP(transdatums[1]), sizeof(Interval)); + + newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, + IntervalPGetDatum(&sumX), + IntervalPGetDatum(newval))); + N.time += 1; + + transdatums[0] = IntervalPGetDatum(newsum); + transdatums[1] = IntervalPGetDatum(&N); + + result = construct_array(transdatums, 2, + false, 12, 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum +interval_avg(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum *transdatums; + int ndatums; + Interval sumX, + N; + + /* We assume the input is array of interval */ + deconstruct_array(transarray, + false, 12, 'd', + &transdatums, &ndatums); + if (ndatums != 2) + elog(ERROR, "interval_avg: expected 2-element interval array"); + /* + * XXX memcpy, instead of just extracting a pointer, to work around + * buggy array code: it won't ensure proper alignment of Interval + * objects on machines where double requires 8-byte alignment. + * That should be fixed, but in the meantime... + */ + memcpy(&sumX, DatumGetIntervalP(transdatums[0]), sizeof(Interval)); + memcpy(&N, DatumGetIntervalP(transdatums[1]), sizeof(Interval)); + + /* SQL92 defines AVG of no values to be NULL */ + if (N.time == 0) + PG_RETURN_NULL(); + + return DirectFunctionCall2(interval_div, + IntervalPGetDatum(&sumX), + Float8GetDatum(N.time)); +} + + /* timestamp_age() * Calculate time difference while retaining year/month fields. * Note that this does not result in an accurate absolute time span |