array_functions-20110215.patch

application/octet-stream

Filename: array_functions-20110215.patch
Type: application/octet-stream
Part: 0
Message: Re: multiset patch review

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
doc/src/sgml/func.sgml 0 0
src/backend/utils/adt/array_userfuncs.c 0 0
src/include/catalog/pg_aggregate.h 0 0
src/include/catalog/pg_proc.h 0 0
src/include/nodes/makefuncs.h 0 0
src/include/utils/array.h 0 0
src/test/regress/expected/arrays.out 0 0
src/test/regress/sql/arrays.sql 0 0
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 70a1bd9..7347de1 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT NULLIF(value, '(none)') ...
*** 10214,10222 ****
--- 10214,10237 ----
      <primary>array_dims</primary>
    </indexterm>
    <indexterm>
+     <primary>array_except</primary>
+   </indexterm>
+   <indexterm>
+     <primary>array_except_all</primary>
+   </indexterm>
+   <indexterm>
      <primary>array_fill</primary>
    </indexterm>
    <indexterm>
+     <primary>array_intersect</primary>
+   </indexterm>
+   <indexterm>
+     <primary>array_intersect_all</primary>
+   </indexterm>
+   <indexterm>
+     <primary>array_is_unique</primary>
+   </indexterm>
+   <indexterm>
      <primary>array_length</primary>
    </indexterm>
    <indexterm>
*************** SELECT NULLIF(value, '(none)') ...
*** 10226,10240 ****
      <primary>array_prepend</primary>
    </indexterm>
    <indexterm>
      <primary>array_to_string</primary>
    </indexterm>
!  <indexterm>
      <primary>array_upper</primary>
    </indexterm>
    <indexterm>
      <primary>string_to_array</primary>
    </indexterm>
    <indexterm>
      <primary>unnest</primary>
    </indexterm>
  
--- 10241,10273 ----
      <primary>array_prepend</primary>
    </indexterm>
    <indexterm>
+     <primary>array_sort</primary>
+   </indexterm>
+   <indexterm>
      <primary>array_to_string</primary>
    </indexterm>
!   <indexterm>
!     <primary>array_union</primary>
!   </indexterm>
!   <indexterm>
!     <primary>array_union_all</primary>
!   </indexterm>
!   <indexterm>
!     <primary>array_unique</primary>
!   </indexterm>
!   <indexterm>
      <primary>array_upper</primary>
    </indexterm>
    <indexterm>
+     <primary>cardinality</primary>
+   </indexterm>
+   <indexterm>
      <primary>string_to_array</primary>
    </indexterm>
    <indexterm>
+     <primary>trim_array</primary>
+   </indexterm>
+   <indexterm>
      <primary>unnest</primary>
    </indexterm>
  
*************** SELECT NULLIF(value, '(none)') ...
*** 10298,10303 ****
--- 10331,10358 ----
         <row>
          <entry>
           <literal>
+           <function>array_except</function>(<type>anyarray</type>, <type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>subtraction of two arrays and remove duplicated values</entry>
+         <entry><literal>array_except(ARRAY[1,1,2], ARRAY[1,3])</literal></entry>
+         <entry><literal>{2}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>array_except_all</function>(<type>anyarray</type>, <type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>subtraction of two arrays</entry>
+         <entry><literal>array_except_all(ARRAY[1,1,2], ARRAY[1,3])</literal></entry>
+         <entry><literal>{1,2}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>array_fill</function>(<type>anyelement</type>, <type>int[]</type>,
            <optional>, <type>int[]</type></optional>)
           </literal>
*************** SELECT NULLIF(value, '(none)') ...
*** 10311,10316 ****
--- 10366,10404 ----
         <row>
          <entry>
           <literal>
+           <function>array_intersect</function>(<type>anyarray</type>, <type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>intersection of two arrays and remove duplicated values</entry>
+         <entry><literal>array_intersect(ARRAY[1,1,2], ARRAY[1,1,3])</literal></entry>
+         <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>array_intersect_all</function>(<type>anyarray</type>, <type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>intersection of two arrays</entry>
+         <entry><literal>array_intersect_all(ARRAY[1,1,2], ARRAY[1,1,3])</literal></entry>
+         <entry><literal>{1,1}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>array_is_unique</function>(<type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>bool</type></entry>
+         <entry>returns true if no duplicated elements in an array</entry>
+         <entry><literal>array_is_unique(ARRAY[1,NULL]), array_is_unique(ARRAY[1,NULL,NULL])</literal></entry>
+         <entry><literal>t, f</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>array_length</function>(<type>anyarray</type>, <type>int</type>)
           </literal>
          </entry>
*************** SELECT NULLIF(value, '(none)') ...
*** 10344,10349 ****
--- 10432,10448 ----
         <row>
          <entry>
           <literal>
+           <function>array_sort</function>(<type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>sort elements in an array in ascending order</entry>
+         <entry><literal>array_sort(ARRAY[3,2,NULL,1])</literal></entry>
+         <entry><literal>{1,2,3,NULL}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>array_to_string</function>(<type>anyarray</type>, <type>text</type> <optional>, <type>text</type></optional>)
           </literal>
          </entry>
*************** SELECT NULLIF(value, '(none)') ...
*** 10356,10361 ****
--- 10455,10493 ----
         <row>
          <entry>
           <literal>
+           <function>array_union</function>(<type>anyarray</type>, <type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>text</type></entry>
+         <entry>union of two arrays and remove duplicated values</entry>
+         <entry><literal>array_union(ARRAY[2,3], ARRAY[1,1,2])</literal></entry>
+         <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>array_union_all</function>(<type>anyarray</type>, <type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>text</type></entry>
+         <entry>union of two arrays, same as <function>array_cat</function></entry>
+         <entry><literal>array_union_all(ARRAY[2,3], ARRAY[1,1,2])</literal></entry>
+         <entry><literal>{2,3,1,1,2}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>array_unique</function>(<type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>remove duplicated elements in an array</entry>
+         <entry><literal>array_unique(ARRAY[1,3,2,3,NULL,1,NULL])</literal></entry>
+         <entry><literal>{1,2,3,NULL}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>array_upper</function>(<type>anyarray</type>, <type>int</type>)
           </literal>
          </entry>
*************** SELECT NULLIF(value, '(none)') ...
*** 10367,10372 ****
--- 10499,10515 ----
         <row>
          <entry>
           <literal>
+           <function>array_trim</function>(<type>anyarray</type>, <type>integer</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>remove supplied number of elements at end of an array</entry>
+         <entry><literal>array_trim(ARRAY[1,2,3], 2), array_trim(ARRAY[[1,2],[3,4]], 1)</literal></entry>
+         <entry><literal>{1}, {{1,2}}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>string_to_array</function>(<type>text</type>, <type>text</type> <optional>, <type>text</type></optional>)
           </literal>
          </entry>
*************** SELECT NULLIF(value, '(none)') ...
*** 10379,10384 ****
--- 10522,10549 ----
         <row>
          <entry>
           <literal>
+           <function>cardinality</function>(<type>anyarray</type>)
+          </literal>
+         </entry>
+         <entry><type>int</type></entry>
+         <entry>returns the number of elements in an array</entry>
+         <entry><literal>cardinality(ARRAY[1,2,3])</literal></entry>
+         <entry><literal>3</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>trim_array</function>(<type>anyarray</type>, <type>integer</type>)
+          </literal>
+         </entry>
+         <entry><type>anyarray</type></entry>
+         <entry>same as <function>array_trim</function></entry>
+         <entry><literal>trim_array(ARRAY[1,2,3], 2), trim_array(ARRAY[[1,2],[3,4]], 1)</literal></entry>
+         <entry><literal>{1}, {{1,2}}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>unnest</function>(<type>anyarray</type>)
           </literal>
          </entry>
*************** SELECT NULLIF(value, '(none)') ...
*** 10420,10428 ****
      </para>
     </note>
  
     <para>
      See also <xref linkend="functions-aggregate"> about the aggregate
!     function <function>array_agg</function> for use with arrays.
     </para>
    </sect1>
  
--- 10585,10605 ----
      </para>
     </note>
  
+    <note>
+     <para>
+      Multidimensional arrays are not supported by
+      <function>array_except</>, <function>array_except_all</>,
+      <function>array_intersect</>, <function>array_intersect_all</>,
+      <function>array_is_unique</>, <function>array_sort</>,
+      <function>array_union</>, <function>array_union_all</>,
+      and <function>array_unique</> in the current version.
+     </para>
+    </note>
+ 
     <para>
      See also <xref linkend="functions-aggregate"> about the aggregate
!     function <function>array_agg</function>, <function>array_fusion</>,
!     and <function>array_intersection</> for use with arrays.
     </para>
    </sect1>
  
*************** SELECT NULLIF(value, '(none)') ...
*** 10468,10474 ****
         <function>array_agg(<replaceable class="parameter">expression</replaceable>)</function>
        </entry>
        <entry>
!        any
        </entry>
        <entry>
         array of the argument type
--- 10645,10651 ----
         <function>array_agg(<replaceable class="parameter">expression</replaceable>)</function>
        </entry>
        <entry>
!        any non-array
        </entry>
        <entry>
         array of the argument type
*************** SELECT NULLIF(value, '(none)') ...
*** 10479,10484 ****
--- 10656,10693 ----
       <row>
        <entry>
         <indexterm>
+         <primary>array_fusion</primary>
+        </indexterm>
+        <function>array_fusion(<replaceable class="parameter">expression</replaceable>)</function>
+       </entry>
+       <entry>
+        any array
+       </entry>
+       <entry>
+        same as argument type
+       </entry>
+       <entry>concatenation of input arrays</entry>
+      </row>
+ 
+      <row>
+       <entry>
+        <indexterm>
+         <primary>array_intersection</primary>
+        </indexterm>
+        <function>array_intersection(<replaceable class="parameter">expression</replaceable>)</function>
+       </entry>
+       <entry>
+        any array
+       </entry>
+       <entry>
+        same as argument type
+       </entry>
+       <entry>intersection of input arrays</entry>
+      </row>
+ 
+      <row>
+       <entry>
+        <indexterm>
          <primary>average</primary>
         </indexterm>
         <indexterm>
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 499d357..9b3276f 100644
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 15,21 ****
--- 15,24 ----
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
+ #include "utils/typcache.h"
  
+ static void check_concatenable(Oid element_type1, Oid element_type2);
+ static void check_comparable(Oid element_type1, Oid element_type2);
  
  /*-----------------------------------------------------------------------------
   * array_push :
*************** array_cat(PG_FUNCTION_ARGS)
*** 218,231 ****
  	element_type2 = ARR_ELEMTYPE(v2);
  
  	/* Check we have matching element types */
! 	if (element_type1 != element_type2)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_DATATYPE_MISMATCH),
! 				 errmsg("cannot concatenate incompatible arrays"),
! 				 errdetail("Arrays with element types %s and %s are not "
! 						   "compatible for concatenation.",
! 						   format_type_be(element_type1),
! 						   format_type_be(element_type2))));
  
  	/* OK, use it */
  	element_type = element_type1;
--- 221,227 ----
  	element_type2 = ARR_ELEMTYPE(v2);
  
  	/* Check we have matching element types */
! 	check_concatenable(element_type1, element_type2);
  
  	/* OK, use it */
  	element_type = element_type1;
*************** array_agg_finalfn(PG_FUNCTION_ARGS)
*** 544,546 ****
--- 540,1328 ----
  
  	PG_RETURN_DATUM(result);
  }
+ 
+ /*
+  * array_cardinality :
+  *		Return the number of elements in an array. If the array is
+  *		multi-demensional, the total number of elements is returned.
+  */
+ Datum
+ array_cardinality(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+ 	int			nitems;
+ 
+ 	nitems = ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v));
+ 
+ 	PG_RETURN_INT32(nitems);
+ }
+ 
+ /*
+  * trim_array :
+  *		Remove elements or slaces at end of an array. It is same as
+  *		array[array_lower(array, 1):array_upper(array, 1) - ntrimmed].
+  */
+ Datum
+ trim_array(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *result;
+ 	ArrayType  *v;
+ 	int32		ntrimmed = PG_GETARG_INT32(1);
+ 	int			ndims;
+ 	int			nitems;
+ 	int			lower[MAXDIM];
+ 	int			upper[MAXDIM];
+ 	int			i;
+ 	int			arrtyplen;
+ 	Oid			elmtype;
+ 	int16		elmlen;
+ 	bool		elmbyval;
+ 	char		elmalign;
+ 
+ 	if (ntrimmed < 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ 				 errmsg("number of trimmed elements (%d) must not be negative", ntrimmed)));
+ 
+ 	v = PG_GETARG_ARRAYTYPE_P(0);
+ 	ndims = ARR_NDIM(v);
+ 
+ 	/* Trimmed elements must not be greater than the first dimension length. */
+ 	nitems = (ndims == 0 ? 0 : ARR_DIMS(v)[0]);
+ 	if (ntrimmed > nitems)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ 				 errmsg("number of trimmed elements (%d) is greater than number of elements (%d)", ntrimmed, nitems)));
+ 
+ 	/* Save lower and upper bounds to restore them at end. */
+ 	for (i = 0; i < ndims; i++)
+ 	{
+ 		lower[i] = ARR_LBOUND(v)[i];
+ 		upper[i] = lower[i] - 1 + ARR_DIMS(v)[i];
+ 	}
+ 
+ 	/* Remove elements from the first dimension and get a new slice. */
+ 	if (ndims > 0)
+ 		upper[0] -= ntrimmed;
+ 	arrtyplen = get_typlen(get_fn_expr_argtype(fcinfo->flinfo, 0));
+ 	elmtype = ARR_ELEMTYPE(v);
+ 	get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+ 	result = array_get_slice(
+ 		v, ndims, upper, lower, arrtyplen, elmlen, elmbyval, elmalign);
+ 
+ 	/* Adjust lower bounds because array_get_slice reset the value to 1. */
+ 	for (i = 0; i < ndims; i++)
+ 		ARR_LBOUND(result)[i] = lower[i];
+ 
+ 	PG_FREE_IF_COPY(v, 0);
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * array_trim :
+  *		An alias for trim_array for naming consistency.
+  */
+ Datum
+ array_trim(PG_FUNCTION_ARGS)
+ {
+ 	return trim_array(fcinfo);
+ }
+ 
+ /*
+  * Find TypeCacheEntry with comparison functions for element_type.
+  * We arrange to look up the compare functions only once per series of
+  * calls, assuming the element type doesn't change underneath us.
+  */
+ static TypeCacheEntry *
+ get_type_cache(Oid element_type, void **fn_extra)
+ {
+ 	TypeCacheEntry *type;
+ 
+ 	type = (TypeCacheEntry *) *fn_extra;
+ 	if (type == NULL ||
+ 		type->type_id != element_type)
+ 	{
+ 		type = lookup_type_cache(element_type,
+ 					TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO);
+ 		if (!OidIsValid(type->eq_opr_finfo.fn_oid) ||
+ 			!OidIsValid(type->cmp_proc_finfo.fn_oid))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 			   errmsg("could not identify comparison functions for type %s",
+ 					  format_type_be(element_type))));
+ 		*fn_extra = type;
+ 	}
+ 
+ 	return type;
+ }
+ 
+ static int
+ compare_elements(const void *a, const void *b, void *arg)
+ {
+ 	return DatumGetInt32(FunctionCall2(
+ 		(FmgrInfo *) arg, *(const Datum *) a, *(const Datum *) b));
+ }
+ 
+ /*
+  * Sort values and move nulls to the end.
+  * Returns number of non-null elements as an option.
+  */
+ static void
+ sort_elements(TypeCacheEntry *type, Datum *values, bool *nulls,
+ 			  int nitems, int *nonnulls)
+ {
+ 	int			n,
+ 				i;
+ 
+ 	if (nulls == NULL)
+ 		n = nitems;
+ 	else
+ 	{
+ 		/* move nulls to end of the array */
+ 		for (i = n = 0; i < nitems; i++)
+ 		{
+ 			if (!nulls[i])
+ 			{
+ 				values[n] = values[i];
+ 				nulls[n] = false;
+ 				n++;
+ 			}
+ 		}
+ 		for (i = n; i < nitems; i++)
+ 			nulls[i] = true;
+ 	}
+ 
+ 	/* sort non-null values */
+ 	qsort_arg(values, n, sizeof(Datum),
+ 			  compare_elements, &type->cmp_proc_finfo);
+ 
+ 	if (nonnulls)
+ 		*nonnulls = n;
+ }
+ 
+ /*
+  * Remove duplicated values in already sorted elements. The values, nulls,
+  * nitems, and nonnulls parameters are modified directly. Note that only
+  * one null value will be kept in the result when there are some nulls.
+  */
+ static void
+ unique_elements(Datum *values, bool *nulls, int *nitems, int *nonnulls,
+ 				TypeCacheEntry *type)
+ {
+ 	int		i,
+ 			n,
+ 			nvalues = *nonnulls;
+ 	bool	has_nulls = (*nonnulls < *nitems);
+ 
+ 	for (i = n = 1; i < nvalues; i++)
+ 	{
+ 		if (!DatumGetBool(FunctionCall2(
+ 			&type->eq_opr_finfo, values[i - 1], values[i])))
+ 		{
+ 			Assert(!nulls[n]);
+ 			values[n++] = values[i];
+ 		}
+ 	}
+ 	*nonnulls = n;
+ 	if (has_nulls)
+ 		nulls[n++] = true;
+ 	*nitems = n;
+ }
+ 
+ /*
+  * Deconstruct an array to a list of elements and sort them. Returns values,
+  * null, number of all elements and non-null elements as output parameters.
+  * nulls and nonnulls can be NULLs.
+  */
+ static void
+ deconstruct_and_sort(ArrayType *array, Datum **values, bool **nulls,
+ 					 int *nitems, int *nonnulls, TypeCacheEntry *type)
+ {
+ 	Oid			element_type = ARR_ELEMTYPE(array);
+ 	bool	   *tmp_nulls;
+ 
+ 	AssertArg(values != NULL);
+ 	AssertArg(nitems != NULL);
+ 
+ 	/* TODO: Currently we do not support multi-dimensional arrays. */
+ 	if (ARR_NDIM(array) > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("cannot use multi-dimensional arrays")));
+ 
+ 	deconstruct_array(array,
+ 					  element_type,
+ 					  type->typlen,
+ 					  type->typbyval,
+ 					  type->typalign,
+ 					  values, &tmp_nulls, nitems);
+ 	sort_elements(type, *values, tmp_nulls, *nitems, nonnulls);
+ 
+ 	if (nulls)
+ 		*nulls = tmp_nulls;
+ }
+ 
+ /*
+  * A worker for array_sort, array_unique, and other functions.
+  * Sort an array in ascending order, and optionally remove duplicated values.
+  */
+ static ArrayType *
+ array_sort_internal(ArrayType *array, bool unique, void **fn_extra)
+ {
+ 	TypeCacheEntry *type;
+ 	Datum		   *values;
+ 	bool		   *nulls;
+ 	int				nitems,
+ 					nonnulls;
+ 	int				ndims = ARR_NDIM(array);
+ 	int				lbs;
+ 	Oid				element_type = ARR_ELEMTYPE(array);
+ 
+ 	/* TODO: Currently we do not support multi-dimensional arrays. */
+ 	if (ndims > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("cannot use multi-dimensional arrays")));
+ 
+ 	/* Save the lower bound. */
+ 	lbs = (ndims > 0 ? ARR_LBOUND(array)[0] : 1);
+ 
+ 	type = get_type_cache(element_type, fn_extra);
+ 	deconstruct_and_sort(array, &values, &nulls, &nitems, &nonnulls, type);
+ 	if (unique)
+ 		unique_elements(values, nulls, &nitems, &nonnulls, type);
+ 
+ 	return construct_md_array(values, nulls, 1, &nitems, &lbs, element_type,
+ 				type->typlen, type->typbyval, type->typalign);
+ }
+ 
+ /*
+  * array_sort :
+  *		Sort an array in ascending order. Nulls are in the last.
+  */
+ Datum
+ array_sort(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
+ 	ArrayType  *result;
+ 
+ 	result = array_sort_internal(array, false, &fcinfo->flinfo->fn_extra);
+ 	PG_FREE_IF_COPY(array, 0);
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * array_unique :
+  *		Remove duplicated elements in an array, and eventually sorted.
+  */
+ Datum
+ array_unique(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
+ 	ArrayType  *result;
+ 
+ 	result = array_sort_internal(array, true, &fcinfo->flinfo->fn_extra);
+ 	PG_FREE_IF_COPY(array, 0);
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * array_is_unique :
+  *		Return true iff an array has not duplicated values.
+  *		Note that only one null is allowed in an unique array.
+  */
+ Datum
+ array_is_unique(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
+ 	bool		result;
+ 	Datum	   *values;
+ 	int			nitems,
+ 				nonnulls;
+ 	int			i;
+ 	TypeCacheEntry *type;
+ 	
+ 	type = get_type_cache(ARR_ELEMTYPE(array), &fcinfo->flinfo->fn_extra);
+ 	deconstruct_and_sort(array, &values, NULL, &nitems, &nonnulls, type);
+ 	if (nitems > nonnulls + 1)
+ 	{
+ 		/* only one null is allowd */
+ 		result = false;
+ 	}
+ 	else
+ 	{
+ 		result = true;
+ 		/* compare for each adjacent */
+ 		for (i = 1; i < nonnulls; i++)
+ 		{
+ 			if (DatumGetBool(FunctionCall2(
+ 				&type->eq_opr_finfo, values[i - 1], values[i])))
+ 			{
+ 				result = false;
+ 				break;
+ 			}
+ 		}
+ 	}
+ 
+ 	PG_FREE_IF_COPY(array, 0);
+ 	PG_RETURN_BOOL(result);
+ }
+ 
+ /*
+  * array_union :
+  *		Concatinate two arrays, and remove duplicated values.
+  */
+ Datum
+ array_union(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *v1,
+ 			   *v2;
+ 	ArrayType  *result;
+ 	Datum	   *values,
+ 			   *values1,
+ 			   *values2;
+ 	bool	   *nulls,
+ 			   *nulls1,
+ 			   *nulls2;
+ 	int			nitems,
+ 				nitems1,
+ 				nitems2,
+ 				nonnulls;
+ 	Oid			element_type1,
+ 				element_type2;
+ 	int			lbs = 1;
+ 	TypeCacheEntry *type;
+ 
+ 	if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
+ 		PG_RETURN_NULL();
+ 
+ 	/* Concatenating a null array is a no-op, just return the other input */
+ 	if (PG_ARGISNULL(0))
+ 	{
+ 		v2 = PG_GETARG_ARRAYTYPE_P(1);
+ 		result = array_sort_internal(v2, true, &fcinfo->flinfo->fn_extra);
+ 		PG_FREE_IF_COPY(v2, 1);
+ 		PG_RETURN_ARRAYTYPE_P(result);
+ 	}
+ 	if (PG_ARGISNULL(1))
+ 	{
+ 		v1 = PG_GETARG_ARRAYTYPE_P(0);
+ 		result = array_sort_internal(v1, true, &fcinfo->flinfo->fn_extra);
+ 		PG_FREE_IF_COPY(v1, 0);
+ 		PG_RETURN_ARRAYTYPE_P(result);
+ 	}
+ 
+ 	v1 = PG_GETARG_ARRAYTYPE_P(0);
+ 	v2 = PG_GETARG_ARRAYTYPE_P(1);
+ 
+ 	/* TODO: Currently we do not support multi-dimensional arrays. */
+ 	if (ARR_NDIM(v1) > 1 || ARR_NDIM(v2) > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("cannot use multi-dimensional arrays")));
+ 
+ 	element_type1 = ARR_ELEMTYPE(v1);
+ 	element_type2 = ARR_ELEMTYPE(v2);
+ 
+ 	check_concatenable(element_type1, element_type2);
+ 	type = get_type_cache(element_type1, &fcinfo->flinfo->fn_extra);
+ 	deconstruct_array(v1,
+ 					  element_type1,
+ 					  type->typlen,
+ 					  type->typbyval,
+ 					  type->typalign,
+ 					  &values1, &nulls1, &nitems1);
+ 	deconstruct_array(v2,
+ 					  element_type2,
+ 					  type->typlen,
+ 					  type->typbyval,
+ 					  type->typalign,
+ 					  &values2, &nulls2, &nitems2);
+ 
+ 	nitems = nitems1 + nitems2;
+ 	values = (Datum *) palloc(sizeof(Datum) * nitems);
+ 	nulls = (bool *) palloc(sizeof(bool) * nitems);
+ 
+ 	memcpy(values, values1, sizeof(Datum) * nitems1);
+ 	memcpy(values + nitems1, values2, sizeof(Datum) * nitems2);
+ 	memcpy(nulls, nulls1, sizeof(bool) * nitems1);
+ 	memcpy(nulls + nitems1, nulls2, sizeof(bool) * nitems2);
+ 
+ 	sort_elements(type, values, nulls, nitems, &nonnulls);
+ 	unique_elements(values, nulls, &nitems, &nonnulls, type);
+ 	result = construct_md_array(values, nulls, 1, &nitems, &lbs,
+ 								element_type1,
+ 								type->typlen,
+ 								type->typbyval,
+ 								type->typalign);
+ 
+ 	PG_FREE_IF_COPY(v1, 0);
+ 	PG_FREE_IF_COPY(v2, 1);
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * array_union_all :
+  *		An alias for array_cat for naming consistency.
+  */
+ Datum
+ array_union_all(PG_FUNCTION_ARGS)
+ {
+ 	return array_cat(fcinfo);
+ }
+ 
+ /*
+  * Intersection of two sorted arrays. The first array is modified directly.
+  * Return length of the result.
+  */
+ static int
+ intersect_sorted_arrays(TypeCacheEntry *type,
+ 						Datum *values1, bool *nulls1, int nitems1,
+ 						const Datum *values2, const bool *nulls2, int nitems2)
+ {
+ 	int			n1,
+ 				n2,
+ 				n;
+ 
+ 	/* add non-nulls */
+ 	for (n = n1 = n2 = 0;
+ 		 n1 < nitems1 && !nulls1[n1] &&
+ 		 n2 < nitems2 && !nulls2[n2];)
+ 	{
+ 		int		r = DatumGetInt32(FunctionCall2(&type->cmp_proc_finfo,
+ 												values1[n1], values2[n2]));
+ 		if (r == 0)
+ 			values1[n++] = values1[n1];
+ 		if (r <= 0)
+ 			n1++;
+ 		if (r >= 0)
+ 			n2++;
+ 	}
+ 
+ 	/* skip non-nulls */
+ 	for (; n1 < nitems1 && !nulls1[n1]; n1++) {}
+ 	for (; n2 < nitems2 && !nulls2[n2]; n2++) {}
+ 
+ 	/* add nulls */
+ 	for (; n1 < nitems1 && n2 < nitems2; n1++, n2++, n++)
+ 		nulls1[n] = true;
+ 
+ 	return n;
+ }
+ 
+ /*
+  * A worker for array_intersect and array_intersect_all.
+  */
+ static Datum
+ array_intersect_internal(PG_FUNCTION_ARGS, bool all)
+ {
+ 	ArrayType  *v1 = PG_GETARG_ARRAYTYPE_P(0);
+ 	ArrayType  *v2 = PG_GETARG_ARRAYTYPE_P(1);
+ 
+ 	ArrayType  *result;
+ 	Oid			element_type = ARR_ELEMTYPE(v1);
+ 	Datum	   *values1,
+ 			   *values2;
+ 	bool	   *nulls1,
+ 			   *nulls2;
+ 	int			nitems1,
+ 				nitems2,
+ 				nonnulls1,
+ 				nonnulls2;
+ 	int			lbs = 1;
+ 	TypeCacheEntry *type;
+ 
+ 	check_comparable(element_type, ARR_ELEMTYPE(v2));
+ 	type = get_type_cache(element_type, &fcinfo->flinfo->fn_extra);
+ 
+ 	deconstruct_and_sort(v1, &values1, &nulls1, &nitems1, &nonnulls1, type);
+ 	if (!all)
+ 		unique_elements(values1, nulls1, &nitems1, &nonnulls1, type);
+ 	deconstruct_and_sort(v2, &values2, &nulls2, &nitems2, &nonnulls2, type);
+ 	if (!all)
+ 		unique_elements(values2, nulls2, &nitems2, &nonnulls2, type);
+ 
+ 	nitems1 = intersect_sorted_arrays(type,
+ 									  values1, nulls1, nitems1,
+ 									  values2, nulls2, nitems2);
+ 	result = construct_md_array(values1, nulls1, 1, &nitems1, &lbs,
+ 								element_type, type->typlen,
+ 								type->typbyval, type->typalign);
+ 
+ 	PG_FREE_IF_COPY(v1, 0);
+ 	PG_FREE_IF_COPY(v2, 1);
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * array_intersect :
+  *		Intersection of two arrays and remove duplicated values.
+  */
+ Datum
+ array_intersect(PG_FUNCTION_ARGS)
+ {
+ 	return array_intersect_internal(fcinfo, false);
+ }
+ 
+ /*
+  * array_intersect_all :
+  *		Intersection of two arrays.
+  */
+ Datum
+ array_intersect_all(PG_FUNCTION_ARGS)
+ {
+ 	return array_intersect_internal(fcinfo, true);
+ }
+ 
+ /*
+  * A worker for array_except and array_except_all.
+  */
+ static Datum
+ array_except_internal(PG_FUNCTION_ARGS, bool all)
+ {
+ 	ArrayType  *v1;
+ 	ArrayType  *v2;
+ 	ArrayType  *result;
+ 	Oid			element_type;
+ 	Datum	   *values1,
+ 			   *values2;
+ 	bool	   *nulls1,
+ 			   *nulls2;
+ 	int			nitems1,
+ 				nitems2,
+ 				nonnulls1,
+ 				nonnulls2;
+ 	int			n1,
+ 				n2,
+ 				n;
+ 	int			lbs = 1;
+ 	TypeCacheEntry *type;
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();
+ 
+ 	v1 = PG_GETARG_ARRAYTYPE_P(0);
+ 
+ 	/* fast path for except null */
+ 	if (PG_ARGISNULL(1))
+ 	{
+ 		result = array_sort_internal(v1, !all, &fcinfo->flinfo->fn_extra);
+ 		PG_FREE_IF_COPY(v1, 0);
+ 		PG_RETURN_ARRAYTYPE_P(result);
+ 	}
+ 
+ 	v2 = PG_GETARG_ARRAYTYPE_P(1);
+ 	element_type = ARR_ELEMTYPE(v1);
+ 	check_concatenable(element_type, ARR_ELEMTYPE(v2));
+ 	type = get_type_cache(element_type, &fcinfo->flinfo->fn_extra);
+ 
+ 	deconstruct_and_sort(v1, &values1, &nulls1, &nitems1, &nonnulls1, type);
+ 	if (!all)
+ 		unique_elements(values1, nulls1, &nitems1, &nonnulls1, type);
+ 	deconstruct_and_sort(v2, &values2, &nulls2, &nitems2, &nonnulls2, type);
+ 	if (!all)
+ 		unique_elements(values2, nulls2, &nitems2, &nonnulls2, type);
+ 
+ 	/* add non-nulls */
+ 	for (n = n1 = n2 = 0; n1 < nonnulls1 && n2 < nonnulls2;)
+ 	{
+ 		int		r = DatumGetInt32(FunctionCall2(&type->cmp_proc_finfo,
+ 												values1[n1], values2[n2]));
+ 		if (r < 0)
+ 			values1[n++] = values1[n1++];
+ 		else
+ 			n2++;
+ 		if (r == 0)
+ 			n1++;
+ 	}
+ 	for (; n1 < nonnulls1; n1++, n++)
+ 		values1[n] = values1[n1];
+ 
+ 	/* add nulls */
+ 	for (n1 = nonnulls1; n1 < nitems1 - (nitems2 - nonnulls2); n1++, n++)
+ 		nulls1[n] = true;
+ 
+ 	result = construct_md_array(values1, nulls1, 1, &n, &lbs, element_type,
+ 								type->typlen, type->typbyval, type->typalign);
+ 
+ 	PG_FREE_IF_COPY(v1, 0);
+ 	PG_FREE_IF_COPY(v2, 1);
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * array_except :
+  *		Subtraction of two arrays and remove duplicated values.
+  */
+ Datum
+ array_except(PG_FUNCTION_ARGS)
+ {
+ 	return array_except_internal(fcinfo, false);
+ }
+ 
+ /*
+  * array_except_all :
+  *		Subtraction of two arrays.
+  */
+ Datum
+ array_except_all(PG_FUNCTION_ARGS)
+ {
+ 	return array_except_internal(fcinfo, true);
+ }
+ 
+ /*
+  * array_fusion :
+  *		Similar to array_agg, but the input values are arrays.
+  */
+ Datum
+ array_fusion_transfn(PG_FUNCTION_ARGS)
+ {
+ 	MemoryContext		aggcontext;
+ 	ArrayBuildState	   *state;
+ 
+ 	if (!AggCheckCallContext(fcinfo, &aggcontext))
+ 	{
+ 		/* cannot be called directly because of internal-type argument */
+ 		elog(ERROR, "array_fusion_transfn called in non-aggregate context");
+ 	}
+ 
+ 	state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ 	if (!PG_ARGISNULL(1))
+ 	{
+ 		ArrayType  *v = PG_GETARG_ARRAYTYPE_P(1);
+ 		Oid			elmtype;
+ 		int16		elmlen;
+ 		bool		elmbyval;
+ 		char		elmalign;
+ 		Datum	   *elems;
+ 		bool	   *nulls;
+ 		int			nitems;
+ 		int			i;
+ 
+ 		if (ARR_NDIM(v) > 1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("cannot use multi-dimensional arrays")));
+ 
+ 		elmtype = ARR_ELEMTYPE(v);
+ 		get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+ 		deconstruct_array(v, elmtype, elmlen, elmbyval, elmalign,
+ 						  &elems, &nulls, &nitems);
+ 		for (i = 0; i < nitems; i++)
+ 			state = accumArrayResult(state, elems[i], nulls[i],
+ 									 elmtype, aggcontext);
+ 
+ 		PG_FREE_IF_COPY(v, 1);
+ 	}
+ 
+ 	PG_RETURN_POINTER(state);
+ }
+ 
+ /*
+  * array_intersection :
+  *		Intersection of all input arrays.
+  */
+ typedef struct ArrayIntersectionState
+ {
+ 	Oid		element_type;
+ 	int		nitems;
+ 	Datum  *values;
+ 	bool   *nulls;
+ } ArrayIntersectionState;
+ 
+ Datum
+ array_intersection_transfn(PG_FUNCTION_ARGS)
+ {
+ 	MemoryContext			aggcontext;
+ 	ArrayIntersectionState *state;
+ 
+ 	if (!AggCheckCallContext(fcinfo, &aggcontext))
+ 	{
+ 		/* cannot be called directly because of internal-type argument */
+ 		elog(ERROR, "array_intersection_transfn called in non-aggregate context");
+ 	}
+ 
+ 	state = PG_ARGISNULL(0) ? NULL
+ 							: (ArrayIntersectionState *) PG_GETARG_POINTER(0);
+ 	if (!PG_ARGISNULL(1))
+ 	{
+ 		ArrayType	   *v = PG_GETARG_ARRAYTYPE_P(1);
+ 		TypeCacheEntry *type;
+ 
+ 		type = get_type_cache(ARR_ELEMTYPE(v), &fcinfo->flinfo->fn_extra);
+ 
+ 		if (state == NULL)
+ 		{
+ 			MemoryContext	oldcontext;
+ 
+ 			oldcontext = MemoryContextSwitchTo(aggcontext);
+ 			state = (ArrayIntersectionState *)
+ 						palloc(sizeof(ArrayIntersectionState));
+ 			state->element_type = ARR_ELEMTYPE(v);
+ 			deconstruct_and_sort(v, &state->values, &state->nulls,
+ 								 &state->nitems, NULL, type);
+ 			MemoryContextSwitchTo(oldcontext);
+ 		}
+ 		else
+ 		{
+ 			Datum	   *values;
+ 			bool	   *nulls;
+ 			int			nitems;
+ 
+ 			check_concatenable(state->element_type, ARR_ELEMTYPE(v));
+ 			deconstruct_and_sort(v, &values, &nulls, &nitems, NULL, type);
+ 			state->nitems = intersect_sorted_arrays(type,
+ 								state->values, state->nulls, state->nitems,
+ 								values, nulls, nitems);
+ 		}
+ 
+ 		PG_FREE_IF_COPY(v, 1);
+ 	}
+ 
+ 	PG_RETURN_POINTER(state);
+ }
+ 
+ Datum
+ array_intersection_finalfn(PG_FUNCTION_ARGS)
+ {
+ 	ArrayIntersectionState *state;
+ 	ArrayType		   *result;
+ 	int					lbs = 1;
+ 	TypeCacheEntry	   *type;
+ 
+ 	state = PG_ARGISNULL(0) ? NULL
+ 							: (ArrayIntersectionState *) PG_GETARG_POINTER(0);
+ 	if (state == NULL)
+ 		PG_RETURN_NULL();
+ 
+ 	type = get_type_cache(state->element_type, &fcinfo->flinfo->fn_extra);
+ 	result = construct_md_array(state->values, state->nulls,
+ 								1, &state->nitems, &lbs, state->element_type,
+ 								type->typlen, type->typbyval, type->typalign);
+ 
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ static void
+ check_concatenable(Oid element_type1, Oid element_type2)
+ {
+ 	if (element_type1 != element_type2)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("cannot concatenate incompatible arrays"),
+ 				 errdetail("Arrays with element types %s and %s are not compatible for concatenation.",
+ 						   format_type_be(element_type1),
+ 						   format_type_be(element_type2))));
+ }
+ 
+ static void
+ check_comparable(Oid element_type1, Oid element_type2)
+ {
+ 	if (element_type1 != element_type2)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("cannot compare incompatible arrays"),
+ 				 errdetail("Arrays with element types %s and %s are not compatible for comparison.",
+ 						   format_type_be(element_type1),
+ 						   format_type_be(element_type2))));
+ }
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 26966d2..83d8312 100644
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
*************** DATA(insert ( 2901 xmlconcat2	  -					0	
*** 222,227 ****
--- 222,229 ----
  
  /* array */
  DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	_null_ ));
+ DATA(insert ( 3160	array_fusion_transfn array_agg_finalfn		0	2281	_null_ ));
+ DATA(insert ( 3163	array_intersection_transfn array_intersection_finalfn	0	2281	_null_ ));
  
  /* text */
  DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	_null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a859885..cb619bf 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 2334 (  array_agg_fina
*** 1062,1067 ****
--- 1062,1101 ----
  DESCR("array_agg final function");
  DATA(insert OID = 2335 (  array_agg		   PGNSP PGUID 12 1 0 0 t f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
  DESCR("concatenate aggregate input into an array");
+ DATA(insert OID = 3147 (  cardinality	   PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2277" _null_ _null_ _null_ _null_ array_cardinality _null_ _null_ _null_ ));
+ DESCR("number of elements in array");
+ DATA(insert OID = 3148 (  trim_array	   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2277 "2277 23" _null_ _null_ _null_ _null_ trim_array _null_ _null_ _null_ ));
+ DESCR("remove elements at end of array");
+ DATA(insert OID = 3149 (  array_trim	   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2277 "2277 23" _null_ _null_ _null_ _null_ array_trim _null_ _null_ _null_ ));
+ DESCR("remove elements at end of array");
+ DATA(insert OID = 3150 (  array_sort	   PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ array_sort _null_ _null_ _null_ ));
+ DESCR("sort an array in ascending order");
+ DATA(insert OID = 3151 (  array_unique	   PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ array_unique _null_ _null_ _null_ ));
+ DESCR("remove duplicated values in an array");
+ DATA(insert OID = 3152 (  array_is_unique  PGNSP PGUID 12 1 0 0 f f f t f i 1 0 16 "2277" _null_ _null_ _null_ _null_ array_is_unique _null_ _null_ _null_ ));
+ DESCR("no duplicated elements?");
+ DATA(insert OID = 3153 (  array_union	   PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_union _null_ _null_ _null_ ));
+ DESCR("concatenate two arrays and remove duplicated values");
+ DATA(insert OID = 3154 (  array_union_all  PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_union_all _null_ _null_ _null_ ));
+ DESCR("concatenate two arrays");
+ DATA(insert OID = 3155 (  array_intersect  PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_intersect _null_ _null_ _null_ ));
+ DESCR("intersection of two arrays and remove duplicated values");
+ DATA(insert OID = 3156 (  array_intersect_all PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_intersect_all _null_ _null_ _null_ ));
+ DESCR("intersection of two arrays");
+ DATA(insert OID = 3157 (  array_except	   PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_except _null_ _null_ _null_ ));
+ DESCR("subtraction of two arrays and remove duplicated values");
+ DATA(insert OID = 3158 (  array_except_all PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_except_all _null_ _null_ _null_ ));
+ DESCR("subtraction of two arrays");
+ DATA(insert OID = 3159 (  array_fusion_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_fusion_transfn _null_ _null_ _null_ ));
+ DESCR("fusion transition function");
+ DATA(insert OID = 3160 (  array_fusion	   PGNSP PGUID 12 1 0 0 t f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("concatenate aggregate arrays into an array");
+ DATA(insert OID = 3161 (  array_intersection_transfn PGNSP PGUID 12 1 0 0 f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_intersection_transfn _null_ _null_ _null_ ));
+ DESCR("intersection transition function");
+ DATA(insert OID = 3162 (  array_intersection_finalfn PGNSP PGUID 12 1 0 0 f f f f f i 1 0 2277 "2281" _null_ _null_ _null_ _null_ array_intersection_finalfn _null_ _null_ _null_ ));
+ DESCR("intersection final function");
+ DATA(insert OID = 3163 (  array_intersection PGNSP PGUID 12 1 0 0 t f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("intersection of all inputs");
  
  DATA(insert OID = 760 (  smgrin			   PGNSP PGUID 12 1 0 0 f f f t f s 1 0 210 "2275" _null_ _null_ _null_ _null_	smgrin _null_ _null_ _null_ ));
  DESCR("I/O");
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 8b7db79..cf7de13 100644
*** a/src/include/nodes/makefuncs.h
--- b/src/include/nodes/makefuncs.h
*************** extern TypeName *makeTypeNameFromOid(Oid
*** 72,77 ****
--- 72,78 ----
  
  extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
  			 List *args, Oid collid, CoercionForm fformat);
+ extern FuncCall *makeFuncCall(List *funcname, List *args, int location);
  
  extern DefElem *makeDefElem(char *name, Node *arg);
  extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 7f7e744..725cc38 100644
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
*************** extern ArrayType *create_singleton_array
*** 281,285 ****
--- 281,300 ----
  
  extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
  extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_cardinality(PG_FUNCTION_ARGS);
+ extern Datum trim_array(PG_FUNCTION_ARGS);
+ extern Datum array_trim(PG_FUNCTION_ARGS);
+ extern Datum array_sort(PG_FUNCTION_ARGS);
+ extern Datum array_unique(PG_FUNCTION_ARGS);
+ extern Datum array_is_unique(PG_FUNCTION_ARGS);
+ extern Datum array_union(PG_FUNCTION_ARGS);
+ extern Datum array_union_all(PG_FUNCTION_ARGS);
+ extern Datum array_intersect(PG_FUNCTION_ARGS);
+ extern Datum array_intersect_all(PG_FUNCTION_ARGS);
+ extern Datum array_except(PG_FUNCTION_ARGS);
+ extern Datum array_except_all(PG_FUNCTION_ARGS);
+ extern Datum array_fusion_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_intersection_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_intersection_finalfn(PG_FUNCTION_ARGS);
  
  #endif   /* ARRAY_H */
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 7b05ce3..334121b 100644
*** a/src/test/regress/expected/arrays.out
--- b/src/test/regress/expected/arrays.out
*************** select * from t1;
*** 1558,1560 ****
--- 1558,1713 ----
   [5:5]={"(42,43)"}
  (1 row)
  
+ -- sort, unique, and set operations
+ SELECT cardinality(ARRAY[1, 2, 3]),
+ 	   cardinality(ARRAY[[1, 2], [3, 4]]);
+  cardinality | cardinality 
+ -------------+-------------
+            3 |           4
+ (1 row)
+ 
+ SELECT trim_array(ARRAY[1, 2, 3], 0),
+        trim_array(ARRAY[1, 2, 3], 2),
+ 	   array_trim('[-2:2]={-2,NULL,0,NULL,2}'::int[], 2),
+ 	   array_trim(ARRAY[[1, 2], [3, 4]], 1);
+  trim_array | trim_array |     array_trim     | array_trim 
+ ------------+------------+--------------------+------------
+  {1,2,3}    | {1}        | [-2:0]={-2,NULL,0} | {{1,2}}
+ (1 row)
+ 
+ SELECT trim_array(ARRAY[1, 2, 3], -1); -- ERROR
+ ERROR:  number of trimmed elements (-1) must not be negative
+ SELECT trim_array(ARRAY[1, 2, 3], 4); -- ERROR
+ ERROR:  number of trimmed elements (4) is greater than number of elements (3)
+ SELECT array_is_unique(ARRAY[1, 2, 3]);
+  array_is_unique 
+ -----------------
+  t
+ (1 row)
+ 
+ SELECT array_is_unique(ARRAY['A', 'A', 'B']);
+  array_is_unique 
+ -----------------
+  f
+ (1 row)
+ 
+ SELECT array_is_unique(ARRAY[1, NULL]),
+ 	   array_is_unique(ARRAY[1, NULL, NULL]);
+  array_is_unique | array_is_unique 
+ -----------------+-----------------
+  t               | f
+ (1 row)
+ 
+ SELECT array_is_unique(ARRAY[[1]]); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_union_all(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+      array_union_all      
+ --------------------------
+  {2,NULL,1,2,NULL,2,NULL}
+ (1 row)
+ 
+ SELECT array_union_all(ARRAY[2, NULL, 1, 2, NULL], NULL);
+   array_union_all  
+ -------------------
+  {2,NULL,1,2,NULL}
+ (1 row)
+ 
+ SELECT array_union(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+  array_union 
+ -------------
+  {1,2,NULL}
+ (1 row)
+ 
+ SELECT array_union(ARRAY[2, NULL, 1, 2, NULL], NULL);
+  array_union 
+ -------------
+  {1,2,NULL}
+ (1 row)
+ 
+ SELECT array_union(ARRAY[[1]], ARRAY[1]); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_intersect_all(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+  array_intersect_all 
+ ---------------------
+  {2,NULL}
+ (1 row)
+ 
+ SELECT array_intersect_all(ARRAY[2, NULL, 1, 2, NULL], NULL);
+  array_intersect_all 
+ ---------------------
+  
+ (1 row)
+ 
+ SELECT array_intersect(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+  array_intersect 
+ -----------------
+  {2,NULL}
+ (1 row)
+ 
+ SELECT array_intersect(ARRAY[2, NULL, 1, 2, NULL], NULL);
+  array_intersect 
+ -----------------
+  
+ (1 row)
+ 
+ SELECT array_intersect(ARRAY[[1]], ARRAY[1]); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_except_all(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+  array_except_all 
+ ------------------
+  {1,2,NULL}
+ (1 row)
+ 
+ SELECT array_except_all(ARRAY[2, NULL, 1, 2, NULL], NULL);
+  array_except_all  
+ -------------------
+  {1,2,2,NULL,NULL}
+ (1 row)
+ 
+ SELECT array_except(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+  array_except 
+ --------------
+  {1}
+ (1 row)
+ 
+ SELECT array_except(ARRAY[2, NULL, 1, 2, NULL], NULL);
+  array_except 
+ --------------
+  {1,2,NULL}
+ (1 row)
+ 
+ SELECT array_except(ARRAY[[1]], ARRAY[1]); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_sort(ARRAY[1, 3, 2, 3, NULL, 1, NULL]),
+ 	   array_sort(ARRAY['A', 'C', 'B', 'C', 'C', 'A']);
+       array_sort       |  array_sort   
+ -----------------------+---------------
+  {1,1,2,3,3,NULL,NULL} | {A,A,B,C,C,C}
+ (1 row)
+ 
+ SELECT array_sort(ARRAY[[1]]); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_unique(ARRAY[1, 3, 2, 3, NULL, 1, NULL]),
+ 	   array_unique(ARRAY['A', 'C', 'B', 'C', 'C', 'A']);
+  array_unique | array_unique 
+ --------------+--------------
+  {1,2,3,NULL} | {A,B,C}
+ (1 row)
+ 
+ SELECT array_unique(ARRAY[[1]]); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_fusion(a), array_intersection(a)
+ 	FROM (VALUES
+ 		(ARRAY[1, 2, 3, 2, 2]),
+ 		(ARRAY[1, 2, 4, 2]),
+ 		(ARRAY[NULL, 3, 2, 1])
+ 	) AS t(a);
+           array_fusion          | array_intersection 
+ --------------------------------+--------------------
+  {1,2,3,2,2,1,2,4,2,NULL,3,2,1} | {1,2}
+ (1 row)
+ 
+ SELECT array_fusion(a) FROM (VALUES (ARRAY[[1]])) AS t(a); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
+ SELECT array_intersection(a) FROM (VALUES (ARRAY[[1]])) AS t(a); -- ERROR
+ ERROR:  cannot use multi-dimensional arrays
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 9ea53b1..90d7aaa 100644
*** a/src/test/regress/sql/arrays.sql
--- b/src/test/regress/sql/arrays.sql
*************** insert into t1 (f1[5].q1) values(42);
*** 438,440 ****
--- 438,485 ----
  select * from t1;
  update t1 set f1[5].q2 = 43;
  select * from t1;
+ 
+ -- sort, unique, and set operations
+ 
+ SELECT cardinality(ARRAY[1, 2, 3]),
+ 	   cardinality(ARRAY[[1, 2], [3, 4]]);
+ SELECT trim_array(ARRAY[1, 2, 3], 0),
+        trim_array(ARRAY[1, 2, 3], 2),
+ 	   array_trim('[-2:2]={-2,NULL,0,NULL,2}'::int[], 2),
+ 	   array_trim(ARRAY[[1, 2], [3, 4]], 1);
+ SELECT trim_array(ARRAY[1, 2, 3], -1); -- ERROR
+ SELECT trim_array(ARRAY[1, 2, 3], 4); -- ERROR
+ SELECT array_is_unique(ARRAY[1, 2, 3]);
+ SELECT array_is_unique(ARRAY['A', 'A', 'B']);
+ SELECT array_is_unique(ARRAY[1, NULL]),
+ 	   array_is_unique(ARRAY[1, NULL, NULL]);
+ SELECT array_is_unique(ARRAY[[1]]); -- ERROR
+ SELECT array_union_all(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+ SELECT array_union_all(ARRAY[2, NULL, 1, 2, NULL], NULL);
+ SELECT array_union(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+ SELECT array_union(ARRAY[2, NULL, 1, 2, NULL], NULL);
+ SELECT array_union(ARRAY[[1]], ARRAY[1]); -- ERROR
+ SELECT array_intersect_all(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+ SELECT array_intersect_all(ARRAY[2, NULL, 1, 2, NULL], NULL);
+ SELECT array_intersect(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+ SELECT array_intersect(ARRAY[2, NULL, 1, 2, NULL], NULL);
+ SELECT array_intersect(ARRAY[[1]], ARRAY[1]); -- ERROR
+ SELECT array_except_all(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+ SELECT array_except_all(ARRAY[2, NULL, 1, 2, NULL], NULL);
+ SELECT array_except(ARRAY[2, NULL, 1, 2, NULL], ARRAY[2, NULL]);
+ SELECT array_except(ARRAY[2, NULL, 1, 2, NULL], NULL);
+ SELECT array_except(ARRAY[[1]], ARRAY[1]); -- ERROR
+ SELECT array_sort(ARRAY[1, 3, 2, 3, NULL, 1, NULL]),
+ 	   array_sort(ARRAY['A', 'C', 'B', 'C', 'C', 'A']);
+ SELECT array_sort(ARRAY[[1]]); -- ERROR
+ SELECT array_unique(ARRAY[1, 3, 2, 3, NULL, 1, NULL]),
+ 	   array_unique(ARRAY['A', 'C', 'B', 'C', 'C', 'A']);
+ SELECT array_unique(ARRAY[[1]]); -- ERROR
+ SELECT array_fusion(a), array_intersection(a)
+ 	FROM (VALUES
+ 		(ARRAY[1, 2, 3, 2, 2]),
+ 		(ARRAY[1, 2, 4, 2]),
+ 		(ARRAY[NULL, 3, 2, 1])
+ 	) AS t(a);
+ SELECT array_fusion(a) FROM (VALUES (ARRAY[[1]])) AS t(a); -- ERROR
+ SELECT array_intersection(a) FROM (VALUES (ARRAY[[1]])) AS t(a); -- ERROR