diff --git a/src/cdata.c b/src/cdata.c
index b929e3ad1196e30de0d05cbb4e93e73645842848..32e8e4d0b8dbad51c5ec428d95b70543faefeca6 100644 (file)
--- a/src/cdata.c
+++ b/src/cdata.c
#include "postrr.h"
#include <errno.h>
+#include <string.h>
#include <postgres.h>
#include <fmgr.h>
/* Postgres utilities */
+#include <catalog/pg_type.h>
#include <utils/array.h>
+enum {
+ CF_AVG = 0,
+ CF_MIN = 1,
+ CF_MAX = 2
+};
+
+#define CF_TO_STR(cf) \
+ (((cf) == CF_AVG) \
+ ? "AVG" \
+ : ((cf) == CF_MIN) \
+ ? "MIN" \
+ : ((cf) == CF_MAX) \
+ ? "MAX" : "UNKNOWN")
+
/*
* data type
*/
struct cdata {
- double value;
+ float8 value;
int32 undef_num;
int32 val_num;
int32 cf;
PG_FUNCTION_INFO_V1(cdata_typmodout);
PG_FUNCTION_INFO_V1(cdata_to_cdata);
+PG_FUNCTION_INFO_V1(int32_to_cdata);
+
+PG_FUNCTION_INFO_V1(cdata_update);
/*
* public API
else
data->cf = 0;
- PG_RETURN_RRTIMESLICE_P(data);
+ PG_RETURN_CDATA_P(data);
} /* cdata_in */
Datum
PG_RETURN_CSTRING(result);
} /* cdata_out */
+Datum
+cdata_typmodin(PG_FUNCTION_ARGS)
+{
+ ArrayType *tm_array;
+
+ Datum *elem_values;
+ int n;
+ char *cf_str;
+ int32 typmod = CF_AVG;
+
+ if (PG_NARGS() != 1)
+ ereport(ERROR, (
+ errmsg("cdata_typmodin() expects one argument"),
+ errhint("Usage: cdata_typmodin(array)")
+ ));
+
+ tm_array = PG_GETARG_ARRAYTYPE_P(0);
+
+ if (ARR_ELEMTYPE(tm_array) != CSTRINGOID)
+ ereport(ERROR, (
+ errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("typmod array must be type cstring[]")
+ ));
+
+ if (ARR_NDIM(tm_array) != 1)
+ ereport(ERROR, (
+ errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("typmod array must be one-dimensional")
+ ));
+
+ deconstruct_array(tm_array, CSTRINGOID,
+ /* elmlen = */ -2, /* elmbyval = */ false, /* elmalign = */ 'c',
+ &elem_values, /* nullsp = */ NULL, &n);
+
+ if (n != 1)
+ ereport(ERROR, (
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cdata typmod array must have one element")
+ ));
+
+ cf_str = DatumGetCString(elem_values[0]);
+ if (! strcasecmp(cf_str, "AVG"))
+ typmod = CF_AVG;
+ else if (! strcasecmp(cf_str, "MIN"))
+ typmod = CF_MIN;
+ else if (! strcasecmp(cf_str, "MAX"))
+ typmod = CF_MAX;
+ else
+ ereport(ERROR, (
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid cdata typmod: %s", cf_str)
+ ));
+
+ PG_RETURN_INT32(typmod);
+} /* cdata_typmodin */
+
+Datum
+cdata_typmodout(PG_FUNCTION_ARGS)
+{
+ int32 typmod;
+ char tm_str[32];
+ char *result;
+
+ if (PG_NARGS() != 1)
+ ereport(ERROR, (
+ errmsg("cdata_typmodout() expects one argument"),
+ errhint("Usage: cdata_typmodout(typmod)")
+ ));
+
+ typmod = PG_GETARG_INT32(0);
+ snprintf(tm_str, sizeof(tm_str), "('%s')", CF_TO_STR(typmod));
+ tm_str[sizeof(tm_str) - 1] = '\0';
+ result = pstrdup(tm_str);
+ PG_RETURN_CSTRING(result);
+} /* cdata_typmodout */
+
+Datum
+cdata_to_cdata(PG_FUNCTION_ARGS)
+{
+ cdata_t *data;
+ int32 typmod;
+
+ if (PG_NARGS() != 3)
+ ereport(ERROR, (
+ errmsg("cdata_to_cdata() "
+ "expects three arguments"),
+ errhint("Usage: cdata_to_cdata"
+ "(cdata, typmod, is_explicit)")
+ ));
+
+ data = PG_GETARG_CDATA_P(0);
+ typmod = PG_GETARG_INT32(1);
+
+ if ((data->cf >= 0) && (data->cf != typmod) && (data->val_num > 1))
+ ereport(ERROR, (
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid cast: cannot cast cdata "
+ "with different typmod (yet)")
+ ));
+
+ data->cf = typmod;
+ PG_RETURN_CDATA_P(data);
+} /* cdata_to_cdata */
+
+Datum
+int32_to_cdata(PG_FUNCTION_ARGS)
+{
+ int32 i_val;
+ int32 typmod;
+
+ cdata_t *data;
+
+ if (PG_NARGS() != 3)
+ ereport(ERROR, (
+ errmsg("int32_to_cdata() expects three arguments"),
+ errhint("Usage: int32_to_cdata"
+ "(integer, typmod, is_explicit)")
+ ));
+
+ i_val = PG_GETARG_INT32(0);
+ typmod = PG_GETARG_INT32(1);
+
+ data = (cdata_t *)palloc0(sizeof(*data));
+
+ data->value = (float8)i_val;
+ data->undef_num = 0;
+ data->val_num = 1;
+
+ if (typmod >= 0)
+ data->cf = typmod;
+ else
+ data->cf = CF_AVG;
+
+ PG_RETURN_CDATA_P(data);
+} /* int32_to_cdata */
+
+Datum
+cdata_update(PG_FUNCTION_ARGS)
+{
+ cdata_t *data;
+ cdata_t *update;
+
+ if (PG_NARGS() != 2)
+ ereport(ERROR, (
+ errmsg("cdata_update() expects two arguments"),
+ errhint("Usage: cdata_update(cdata, cdata)")
+ ));
+
+ data = PG_GETARG_CDATA_P(0);
+ update = PG_GETARG_CDATA_P(1);
+
+ if ((data->cf != update->cf) && (update->val_num > 1))
+ ereport(ERROR, (
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid update value: incompatible "
+ "consolidation function")
+ ));
+
+ switch (data->cf) {
+ case CF_AVG:
+ data->value = (data->value * (data->val_num - data->undef_num))
+ + (update->value * (update->val_num - update->undef_num));
+ data->value /= (data->val_num - data->undef_num)
+ + (update->val_num - update->undef_num);
+ break;
+ case CF_MIN:
+ data->value = (data->value <= update->value)
+ ? data->value : update->value;
+ break;
+ case CF_MAX:
+ data->value = (data->value >= update->value)
+ ? data->value : update->value;
+ break;
+ default:
+ ereport(ERROR, (
+ errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg("unknown consolidation function %d",
+ data->cf)
+ ));
+ break;
+ }
+
+ data->undef_num += update->undef_num;
+ data->val_num += update->val_num;
+ PG_RETURN_CDATA_P(data);
+} /* cdata_update */
+
/* vim: set tw=78 sw=4 ts=4 noexpandtab : */