From 32cc9e553323c40beba4a98a5859fafe6e4e1855 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Fri, 14 Nov 2008 19:58:45 +0000 Subject: [PATCH] Reduce contrib/intagg to a thin wrapper around the new core functions array_agg() and unnest(). We could drop it entirely in the future, but let's keep it for a release or two as a compatibility assist. --- contrib/intagg/Makefile | 6 +- contrib/intagg/int_aggregate.c | 278 ------------------ ...int_aggregate.sql.in => int_aggregate.sql} | 22 +- contrib/intagg/uninstall_int_aggregate.sql | 6 +- doc/src/sgml/intagg.sgml | 34 +-- 5 files changed, 25 insertions(+), 321 deletions(-) delete mode 100644 contrib/intagg/int_aggregate.c rename contrib/intagg/{int_aggregate.sql.in => int_aggregate.sql} (63%) diff --git a/contrib/intagg/Makefile b/contrib/intagg/Makefile index 8855f36f61d..d100ec91afa 100644 --- a/contrib/intagg/Makefile +++ b/contrib/intagg/Makefile @@ -2,11 +2,9 @@ # Makefile for integer aggregator # Copyright (C) 2001 Digital Music Network. # by Mark L. Woodward -# $PostgreSQL: pgsql/contrib/intagg/Makefile,v 1.9 2007/11/10 23:59:51 momjian Exp $ +# $PostgreSQL: pgsql/contrib/intagg/Makefile,v 1.10 2008/11/14 19:58:45 tgl Exp $ -MODULES = int_aggregate -DATA_built = int_aggregate.sql -DATA = uninstall_int_aggregate.sql +DATA = int_aggregate.sql uninstall_int_aggregate.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/intagg/int_aggregate.c b/contrib/intagg/int_aggregate.c deleted file mode 100644 index 4ad43dc8c02..00000000000 --- a/contrib/intagg/int_aggregate.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Integer array aggregator / enumerator - * - * Mark L. Woodward - * DMN Digital Music Network. - * www.dmn.com - * - * $PostgreSQL: pgsql/contrib/intagg/int_aggregate.c,v 1.27 2008/05/12 00:00:42 alvherre Exp $ - * - * Copyright (C) Digital Music Network - * December 20, 2001 - * - * This file is the property of the Digital Music Network (DMN). - * It is being made available to users of the PostgreSQL system - * under the BSD license. - */ -#include "postgres.h" - -#include <ctype.h> -#include <sys/types.h> - -#include "access/xact.h" -#include "catalog/indexing.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "executor/executor.h" -#include "fmgr.h" -#include "miscadmin.h" -#include "utils/array.h" -#include "utils/builtins.h" -#include "utils/memutils.h" -#include "utils/syscache.h" -#include "utils/lsyscache.h" - -PG_MODULE_MAGIC; - -/* - * This is actually a postgres version of a one dimensional array. - * We cheat a little by using the lower-bound field as an indicator - * of the physically allocated size, while the dimensionality is the - * count of items accumulated so far. - */ -typedef struct -{ - ArrayType a; - int items; - int lower; - int4 array[1]; -} PGARRAY; - -/* This is used to keep track of our position during enumeration */ -typedef struct callContext -{ - PGARRAY *p; - int num; - int flags; -} CTX; - -#define TOASTED 1 -#define START_NUM 8 /* initial size of arrays */ -#define PGARRAY_SIZE(n) (sizeof(PGARRAY) + (((n)-1)*sizeof(int4))) - -static PGARRAY *GetPGArray(PGARRAY * p, AggState *aggstate, bool fAdd); -static PGARRAY *ShrinkPGArray(PGARRAY * p); - -Datum int_agg_state(PG_FUNCTION_ARGS); -Datum int_agg_final_array(PG_FUNCTION_ARGS); -Datum int_enum(PG_FUNCTION_ARGS); - -PG_FUNCTION_INFO_V1(int_agg_state); -PG_FUNCTION_INFO_V1(int_agg_final_array); -PG_FUNCTION_INFO_V1(int_enum); - -/* - * Manage the allocation state of the array - * - * Note that the array needs to be in a reasonably long-lived context, - * ie the Agg node's aggcontext. - */ -static PGARRAY * -GetPGArray(PGARRAY * p, AggState *aggstate, bool fAdd) -{ - if (!p) - { - /* New array */ - int cb = PGARRAY_SIZE(START_NUM); - - p = (PGARRAY *) MemoryContextAlloc(aggstate->aggcontext, cb); - SET_VARSIZE(p, cb); - p->a.ndim = 1; - p->a.dataoffset = 0; /* we don't support nulls, for now */ - p->a.elemtype = INT4OID; - p->items = 0; - p->lower = START_NUM; - } - else if (fAdd) - { - /* Ensure array has space for another item */ - if (p->items >= p->lower) - { - PGARRAY *pn; - int n = p->lower * 2; - int cbNew = PGARRAY_SIZE(n); - - pn = (PGARRAY *) MemoryContextAlloc(aggstate->aggcontext, cbNew); - memcpy(pn, p, VARSIZE(p)); - SET_VARSIZE(pn, cbNew); - pn->lower = n; - /* do not pfree(p), because nodeAgg.c will */ - p = pn; - } - } - return p; -} - -/* - * Shrinks the array to its actual size and moves it into the standard - * memory allocation context - */ -static PGARRAY * -ShrinkPGArray(PGARRAY * p) -{ - PGARRAY *pnew; - - /* get target size */ - int cb = PGARRAY_SIZE(p->items); - - /* use current transaction context */ - pnew = palloc(cb); - memcpy(pnew, p, cb); - - /* fix up the fields in the new array to match normal conventions */ - SET_VARSIZE(pnew, cb); - pnew->lower = 1; - - /* do not pfree(p), because nodeAgg.c will */ - - return pnew; -} - -/* Called for each iteration during an aggregate function */ -Datum -int_agg_state(PG_FUNCTION_ARGS) -{ - PGARRAY *state; - PGARRAY *p; - - /* - * As of PG 8.1 we can actually verify that we are being used as an - * aggregate function, and so it is safe to scribble on our left input. - */ - if (!(fcinfo->context && IsA(fcinfo->context, AggState))) - elog(ERROR, "int_agg_state may only be used as an aggregate"); - - if (PG_ARGISNULL(0)) - state = NULL; /* first time through */ - else - state = (PGARRAY *) PG_GETARG_POINTER(0); - p = GetPGArray(state, (AggState *) fcinfo->context, true); - - if (!PG_ARGISNULL(1)) - { - int4 value = PG_GETARG_INT32(1); - - if (!p) /* internal error */ - elog(ERROR, "no aggregate storage"); - else if (p->items >= p->lower) /* internal error */ - elog(ERROR, "aggregate storage too small"); - else - p->array[p->items++] = value; - } - PG_RETURN_POINTER(p); -} - -/* - * This is the final function used for the integer aggregator. It returns all - * the integers collected as a one dimensional integer array - */ -Datum -int_agg_final_array(PG_FUNCTION_ARGS) -{ - PGARRAY *state; - PGARRAY *p; - PGARRAY *pnew; - - /* - * As of PG 8.1 we can actually verify that we are being used as an - * aggregate function, and so it is safe to scribble on our left input. - */ - if (!(fcinfo->context && IsA(fcinfo->context, AggState))) - elog(ERROR, "int_agg_final_array may only be used as an aggregate"); - - if (PG_ARGISNULL(0)) - state = NULL; /* zero items in aggregation */ - else - state = (PGARRAY *) PG_GETARG_POINTER(0); - p = GetPGArray(state, (AggState *) fcinfo->context, false); - - pnew = ShrinkPGArray(p); - PG_RETURN_POINTER(pnew); -} - -/* - * This function accepts an array, and returns one item for each entry in the - * array - */ -Datum -int_enum(PG_FUNCTION_ARGS) -{ - PGARRAY *p = (PGARRAY *) PG_GETARG_POINTER(0); - CTX *pc; - ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (!rsi || !IsA(rsi, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("int_enum called in context that cannot accept a set"))); - - if (!p) - { - elog(WARNING, "no data sent"); - PG_RETURN_NULL(); - } - - if (!fcinfo->flinfo->fn_extra) - { - /* Allocate working state */ - MemoryContext oldcontext; - - oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); - - pc = (CTX *) palloc(sizeof(CTX)); - - /* Don't copy attribute if you don't need to */ - if (VARATT_IS_EXTENDED(p)) - { - /* Toasted!!! */ - pc->p = (PGARRAY *) PG_DETOAST_DATUM_COPY(p); - pc->flags = TOASTED; - } - else - { - /* Untoasted */ - pc->p = p; - pc->flags = 0; - } - /* Now that we have a detoasted array, verify dimensions */ - /* We'll treat a zero-D array as empty, below */ - if (pc->p->a.ndim > 1) - elog(ERROR, "int_enum only accepts 1-D arrays"); - pc->num = 0; - fcinfo->flinfo->fn_extra = (void *) pc; - MemoryContextSwitchTo(oldcontext); - } - else - /* use existing working state */ - pc = (CTX *) fcinfo->flinfo->fn_extra; - - /* Are we done yet? */ - if (pc->p->a.ndim < 1 || pc->num >= pc->p->items) - { - /* We are done */ - if (pc->flags & TOASTED) - pfree(pc->p); - pfree(pc); - fcinfo->flinfo->fn_extra = NULL; - rsi->isDone = ExprEndResult; - } - else - { - /* nope, return the next value */ - int val = pc->p->array[pc->num++]; - - rsi->isDone = ExprMultipleResult; - PG_RETURN_INT32(val); - } - PG_RETURN_NULL(); -} diff --git a/contrib/intagg/int_aggregate.sql.in b/contrib/intagg/int_aggregate.sql similarity index 63% rename from contrib/intagg/int_aggregate.sql.in rename to contrib/intagg/int_aggregate.sql index 4857752f79d..7ed7c5ddd2d 100644 --- a/contrib/intagg/int_aggregate.sql.in +++ b/contrib/intagg/int_aggregate.sql @@ -1,28 +1,28 @@ -/* $PostgreSQL: pgsql/contrib/intagg/int_aggregate.sql.in,v 1.9 2007/11/13 04:24:28 momjian Exp $ */ +/* $PostgreSQL: pgsql/contrib/intagg/int_aggregate.sql,v 1.1 2008/11/14 19:58:45 tgl Exp $ */ -- Adjust this setting to control where the objects get created. SET search_path = public; -- Internal function for the aggregate -- Is called for each item in an aggregation -CREATE OR REPLACE FUNCTION int_agg_state (int4[], int4) -RETURNS int4[] -AS 'MODULE_PATHNAME','int_agg_state' -LANGUAGE C; +CREATE OR REPLACE FUNCTION int_agg_state (internal, int4) +RETURNS internal +AS 'array_agg_transfn' +LANGUAGE INTERNAL; -- Internal function for the aggregate -- Is called at the end of the aggregation, and returns an array. -CREATE OR REPLACE FUNCTION int_agg_final_array (int4[]) +CREATE OR REPLACE FUNCTION int_agg_final_array (internal) RETURNS int4[] -AS 'MODULE_PATHNAME','int_agg_final_array' -LANGUAGE C; +AS 'array_agg_finalfn' +LANGUAGE INTERNAL; -- The aggregate function itself -- uses the above functions to create an array of integers from an aggregation. CREATE AGGREGATE int_array_aggregate ( BASETYPE = int4, SFUNC = int_agg_state, - STYPE = int4[], + STYPE = internal, FINALFUNC = int_agg_final_array ); @@ -31,5 +31,5 @@ CREATE AGGREGATE int_array_aggregate ( -- as a row. CREATE OR REPLACE FUNCTION int_array_enum(int4[]) RETURNS setof integer -AS 'MODULE_PATHNAME','int_enum' -LANGUAGE C IMMUTABLE STRICT; +AS 'array_unnest' +LANGUAGE INTERNAL IMMUTABLE STRICT; diff --git a/contrib/intagg/uninstall_int_aggregate.sql b/contrib/intagg/uninstall_int_aggregate.sql index 188e32c6604..fe0f4c7df4e 100644 --- a/contrib/intagg/uninstall_int_aggregate.sql +++ b/contrib/intagg/uninstall_int_aggregate.sql @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/contrib/intagg/uninstall_int_aggregate.sql,v 1.3 2007/11/13 04:24:28 momjian Exp $ */ +/* $PostgreSQL: pgsql/contrib/intagg/uninstall_int_aggregate.sql,v 1.4 2008/11/14 19:58:45 tgl Exp $ */ -- Adjust this setting to control where the objects get dropped. SET search_path = public; @@ -7,6 +7,6 @@ DROP FUNCTION int_array_enum(int4[]); DROP AGGREGATE int_array_aggregate (int4); -DROP FUNCTION int_agg_final_array (int4[]); +DROP FUNCTION int_agg_final_array (internal); -DROP FUNCTION int_agg_state (int4[], int4); +DROP FUNCTION int_agg_state (internal, int4); diff --git a/doc/src/sgml/intagg.sgml b/doc/src/sgml/intagg.sgml index b6f37f752cf..b33e7c62de7 100644 --- a/doc/src/sgml/intagg.sgml +++ b/doc/src/sgml/intagg.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/intagg.sgml,v 1.3 2007/12/10 05:32:51 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/intagg.sgml,v 1.4 2008/11/14 19:58:45 tgl Exp $ --> <sect1 id="intagg"> <title>intagg</title> @@ -9,7 +9,10 @@ <para> The <filename>intagg</filename> module provides an integer aggregator and an - enumerator. + enumerator. <filename>intagg</filename> is now obsolete, because there + are built-in functions that provide a superset of its capabilities. + However, the module is still provided as a compatibility wrapper around + the built-in functions. </para> <sect2> @@ -20,38 +23,19 @@ <function>int_array_aggregate(integer)</> that produces an integer array containing exactly the integers it is fed. - Here is a not-tremendously-useful example: + This is a wrapper around <function>array_agg</>, + which does the same thing for any array type. </para> - <programlisting> -test=# select int_array_aggregate(i) from -test-# generate_series(1,10,2) i; - int_array_aggregate ---------------------- - {1,3,5,7,9} -(1 row) - </programlisting> - <para> The enumerator is a function <function>int_array_enum(integer[])</> that returns <type>setof integer</>. It is essentially the reverse operation of the aggregator: given an array of integers, expand it - into a set of rows. For example, + into a set of rows. This is a wrapper around <function>unnest</>, + which does the same thing for any array type. </para> - <programlisting> -test=# select * from int_array_enum(array[1,3,5,7,9]); - int_array_enum ----------------- - 1 - 3 - 5 - 7 - 9 -(5 rows) - </programlisting> - </sect2> <sect2> -- GitLab