Code

CData: Added cdata_update().
[postrr.git] / src / cdata.c
index b929e3ad1196e30de0d05cbb4e93e73645842848..32e8e4d0b8dbad51c5ec428d95b70543faefeca6 100644 (file)
 #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;
@@ -62,6 +78,9 @@ PG_FUNCTION_INFO_V1(cdata_typmodin);
 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
@@ -150,7 +169,7 @@ cdata_in(PG_FUNCTION_ARGS)
        else
                data->cf = 0;
 
-       PG_RETURN_RRTIMESLICE_P(data);
+       PG_RETURN_CDATA_P(data);
 } /* cdata_in */
 
 Datum
@@ -176,5 +195,192 @@ cdata_out(PG_FUNCTION_ARGS)
        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 : */