From 5ecfed8aa54f55586cfc0ba204ec172ae44a5f13 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sun, 29 Apr 2012 20:49:12 +0200 Subject: [PATCH] CData: Added support for a type modifier. The type modifier may be any of 'MIN', 'AVG', 'MAX', specifying the consolidation function to be used. --- src/cdata.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++ src/postrr.h.in | 8 ++++ src/postrr.sql.in | 21 ++++++++ 3 files changed, 149 insertions(+) diff --git a/src/cdata.c b/src/cdata.c index 076b037..432aba3 100644 --- a/src/cdata.c +++ b/src/cdata.c @@ -32,13 +32,29 @@ #include "postrr.h" #include +#include #include #include /* Postgres utilities */ +#include #include +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 */ @@ -176,5 +192,109 @@ 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 */ + /* vim: set tw=78 sw=4 ts=4 noexpandtab : */ diff --git a/src/postrr.h.in b/src/postrr.h.in index b2c9ad2..95582db 100644 --- a/src/postrr.h.in +++ b/src/postrr.h.in @@ -120,6 +120,14 @@ Datum cdata_in(PG_FUNCTION_ARGS); Datum cdata_out(PG_FUNCTION_ARGS); +Datum +cdata_typmodin(PG_FUNCTION_ARGS); +Datum +cdata_typmodout(PG_FUNCTION_ARGS); + +/* casts */ +Datum +cdata_to_cdata(PG_FUNCTION_ARGS); #endif /* ! POSTRR_H */ diff --git a/src/postrr.sql.in b/src/postrr.sql.in index 22ce15c..cf24fce 100644 --- a/src/postrr.sql.in +++ b/src/postrr.sql.in @@ -219,14 +219,35 @@ CREATE OR REPLACE FUNCTION CData_out(CData) AS 'postrr-@POSTRR_MAJOR_VERSION@.@POSTRR_MINOR_VERSION@', 'cdata_out' LANGUAGE 'C' IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION CData_typmodin(cstring[]) + RETURNS integer + AS 'postrr-@POSTRR_MAJOR_VERSION@.@POSTRR_MINOR_VERSION@', 'cdata_typmodin' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION CData_typmodout(integer) + RETURNS cstring + AS 'postrr-@POSTRR_MAJOR_VERSION@.@POSTRR_MINOR_VERSION@', 'cdata_typmodout' + LANGUAGE 'C' IMMUTABLE STRICT; + CREATE TYPE CData ( INTERNALLENGTH = 24, INPUT = CData_in, OUTPUT = CData_out, + TYPMOD_IN = CData_typmodin, + TYPMOD_OUT = CData_typmodout, ALIGNMENT = double, STORAGE = plain ); +CREATE OR REPLACE FUNCTION CData(cdata, integer, boolean) + RETURNS cdata + AS 'postrr-@POSTRR_MAJOR_VERSION@.@POSTRR_MINOR_VERSION@', 'cdata_to_cdata' + LANGUAGE 'C' IMMUTABLE STRICT; + +CREATE CAST (cdata AS cdata) + WITH FUNCTION CData(cdata, integer, boolean) + AS IMPLICIT; + COMMIT; SET client_min_messages TO DEFAULT; -- 2.30.2