mirror of
https://github.com/postgres/postgres.git
synced 2025-11-12 05:01:15 +03:00
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...
This commit is contained in:
@@ -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));
|
||||
memcpy((char *) ARR_DIMS(retval), (char *) dim,
|
||||
ndim * sizeof(int));
|
||||
memcpy((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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* ====================================
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -650,14 +650,6 @@ int2div(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INT16(arg1 / arg2);
|
||||
}
|
||||
|
||||
Datum
|
||||
int2inc(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 arg = PG_GETARG_INT16(0);
|
||||
|
||||
PG_RETURN_INT16(arg + 1);
|
||||
}
|
||||
|
||||
Datum
|
||||
int24pl(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
@@ -1230,49 +1231,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() -
|
||||
*
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1939,6 +1897,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++;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user