*** ./doc/src/sgml/func.sgml.orig	2010-04-07 08:12:52.000000000 +0200
--- ./doc/src/sgml/func.sgml	2010-05-05 20:36:31.692244426 +0200
***************
*** 4639,4645 ****
     </para>
  
     <para>
!     If case-independent matching is specified,
      the effect is much as if all case distinctions had vanished from the
      alphabet.
      When an alphabetic that exists in multiple cases appears as an
--- 4639,4645 ----
     </para>
  
     <para>
!      If case-independent matching is specified,
      the effect is much as if all case distinctions had vanished from the
      alphabet.
      When an alphabetic that exists in multiple cases appears as an
***************
*** 9501,9506 ****
--- 9501,9512 ----
      <primary>string_to_array</primary>
    </indexterm>
    <indexterm>
+     <primary>to_array</primary>
+   </indexterm>
+   <indexterm>
+     <primary>to_string</primary>
+   </indexterm>
+   <indexterm>
      <primary>unnest</primary>
    </indexterm>
  
***************
*** 9643,9648 ****
--- 9649,9676 ----
         <row>
          <entry>
           <literal>
+           <function>to_array</function>(<type>text</type>, <type>text</type> <optional>, <type>text</type></optional>)
+          </literal>
+         </entry>
+         <entry><type>text[]</type></entry>
+         <entry>splits string into array elements using supplied delimiter and null string</entry>
+         <entry><literal>to_array('1,2,3,,5', ',')</literal></entry>
+         <entry><literal>{1,2,3,4,NULL,5}</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
+           <function>to_string</function>(<type>anyarray</type>, <type>text</type> <optional>, <type>text</type></optional>)
+          </literal>
+         </entry>
+         <entry><type>text</type></entry>
+         <entry>concatenates array elements using supplied delimiter and null string</entry>
+         <entry><literal>to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*')</literal></entry>
+         <entry><literal>1,2,3,*,5</literal></entry>
+        </row>
+        <row>
+         <entry>
+          <literal>
            <function>unnest</function>(<type>anyarray</type>)
           </literal>
          </entry>
*** ./src/backend/catalog/system_views.sql.orig	2010-04-26 16:22:37.000000000 +0200
--- ./src/backend/catalog/system_views.sql	2010-05-05 19:59:05.256243744 +0200
***************
*** 487,489 ****
--- 487,497 ----
  CREATE OR REPLACE FUNCTION
    pg_start_backup(label text, fast boolean DEFAULT false)
    RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
+ 
+ CREATE OR REPLACE FUNCTION
+   to_string(v anyarray, fldsep text, null_string text DEFAULT '')
+   RETURNS text STRICT IMMUTABLE LANGUAGE internal AS 'to_string';
+ 
+ CREATE OR REPLACE FUNCTION
+   to_array(inputstr text, fldsep text, null_string text DEFAULT '')
+   RETURNS text[] STRICT IMMUTABLE LANGUAGE internal AS 'to_array';
*** ./src/backend/utils/adt/array_userfuncs.c.orig	2010-02-26 03:01:06.000000000 +0100
--- ./src/backend/utils/adt/array_userfuncs.c	2010-05-05 19:15:45.898243688 +0200
***************
*** 407,415 ****
--- 407,417 ----
  create_singleton_array(FunctionCallInfo fcinfo,
  					   Oid element_type,
  					   Datum element,
+ 					   bool isNull,
  					   int ndims)
  {
  	Datum		dvalues[1];
+ 	bool		nulls[1];
  	int16		typlen;
  	bool		typbyval;
  	char		typalign;
***************
*** 429,434 ****
--- 431,437 ----
  						ndims, MAXDIM)));
  
  	dvalues[0] = element;
+ 	nulls[0] = isNull;
  
  	for (i = 0; i < ndims; i++)
  	{
***************
*** 462,468 ****
  	typbyval = my_extra->typbyval;
  	typalign = my_extra->typalign;
  
! 	return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
  							  typlen, typbyval, typalign);
  }
  
--- 465,471 ----
  	typbyval = my_extra->typbyval;
  	typalign = my_extra->typalign;
  
! 	return construct_md_array(dvalues, nulls, ndims, dims, lbs, element_type,
  							  typlen, typbyval, typalign);
  }
  
*** ./src/backend/utils/adt/varlena.c.orig	2010-02-26 03:01:10.000000000 +0100
--- ./src/backend/utils/adt/varlena.c	2010-05-05 20:17:53.657243603 +0200
***************
*** 2965,2980 ****
  }
  
  /*
!  * text_to_array
!  * parse input string
!  * return text array of elements
!  * based on provided field separator
   */
! Datum
! text_to_array(PG_FUNCTION_ARGS)
  {
- 	text	   *inputstring = PG_GETARG_TEXT_PP(0);
- 	text	   *fldsep = PG_GETARG_TEXT_PP(1);
  	int			inputstring_len;
  	int			fldsep_len;
  	TextPositionState state;
--- 2965,2986 ----
  }
  
  /*
!  * Returns true when two text params are same.
   */
! static 
! bool text_isequal(text *txt1, text *txt2)
! {
! 	return DatumGetBool(DirectFunctionCall2(texteq,
! 							PointerGetDatum(txt1),
! 							PointerGetDatum(txt2)));
! }
! 
! /*
!  * common code for text_to_array and to_array functions
!  */
! static Datum 
! _text_to_array(FunctionCallInfo fcinfo, text *inputstring, text *fldsep, text *null_string, bool *isNull)
  {
  	int			inputstring_len;
  	int			fldsep_len;
  	TextPositionState state;
***************
*** 2985,2993 ****
  	char	   *start_ptr;
  	text	   *result_text;
  	ArrayBuildState *astate = NULL;
  
  	text_position_setup(inputstring, fldsep, &state);
! 
  	/*
  	 * Note: we check the converted string length, not the original, because
  	 * they could be different if the input contained invalid encoding.
--- 2991,3000 ----
  	char	   *start_ptr;
  	text	   *result_text;
  	ArrayBuildState *astate = NULL;
+ 	bool		is_null_string;
  
  	text_position_setup(inputstring, fldsep, &state);
! 	
  	/*
  	 * Note: we check the converted string length, not the original, because
  	 * they could be different if the input contained invalid encoding.
***************
*** 2999,3005 ****
  	if (inputstring_len < 1)
  	{
  		text_position_cleanup(&state);
! 		PG_RETURN_NULL();
  	}
  
  	/*
--- 3006,3013 ----
  	if (inputstring_len < 1)
  	{
  		text_position_cleanup(&state);
! 		*isNull = true;
! 		return (Datum) 0;
  	}
  
  	/*
***************
*** 3009,3016 ****
  	if (fldsep_len < 1)
  	{
  		text_position_cleanup(&state);
! 		PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
! 										   PointerGetDatum(inputstring), 1));
  	}
  
  	start_posn = 1;
--- 3017,3026 ----
  	if (fldsep_len < 1)
  	{
  		text_position_cleanup(&state);
! 		is_null_string = null_string != NULL ? text_isequal(null_string, inputstring) : false;
! 		*isNull = false;
! 		return (Datum) create_singleton_array(fcinfo, TEXTOID,
! 								PointerGetDatum(inputstring), is_null_string, 1);
  	}
  
  	start_posn = 1;
***************
*** 3036,3046 ****
  
  		/* must build a temp text datum to pass to accumArrayResult */
  		result_text = cstring_to_text_with_len(start_ptr, chunk_len);
! 
  		/* stash away this field */
  		astate = accumArrayResult(astate,
  								  PointerGetDatum(result_text),
! 								  false,
  								  TEXTOID,
  								  CurrentMemoryContext);
  
--- 3046,3057 ----
  
  		/* must build a temp text datum to pass to accumArrayResult */
  		result_text = cstring_to_text_with_len(start_ptr, chunk_len);
! 		is_null_string = null_string != NULL ? text_isequal(null_string, result_text) : false;
! 		
  		/* stash away this field */
  		astate = accumArrayResult(astate,
  								  PointerGetDatum(result_text),
! 								  is_null_string,
  								  TEXTOID,
  								  CurrentMemoryContext);
  
***************
*** 3057,3076 ****
  
  	text_position_cleanup(&state);
  
! 	PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
! 										  CurrentMemoryContext));
  }
  
  /*
!  * array_to_text
!  * concatenate Cstring representation of input array elements
!  * using provided field separator
   */
  Datum
! array_to_text(PG_FUNCTION_ARGS)
  {
- 	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
- 	char	   *fldsep = text_to_cstring(PG_GETARG_TEXT_PP(1));
  	int			nitems,
  			   *dims,
  				ndims;
--- 3068,3129 ----
  
  	text_position_cleanup(&state);
  
! 	*isNull = false;
! 	return makeArrayResult(astate, CurrentMemoryContext);
  }
  
+ 
  /*
!  * text_to_array
!  * parse input string
!  * return text array of elements
!  * based on provided field separator
   */
  Datum
! text_to_array(PG_FUNCTION_ARGS)
! {
! 	text	   *inputstring = PG_GETARG_TEXT_PP(0);
! 	text	   *fldsep = PG_GETARG_TEXT_PP(1);
! 	bool		isNull;
! 	Datum	result;
! 	
! 	result = _text_to_array(fcinfo, inputstring, fldsep, NULL, &isNull);
! 	
! 	if (isNull)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_ARRAYTYPE_P(result);
! }
! 
! /*
!  * to_array
!  * Parse string, returns array. string defined as nullsym is replaced by NULL.
!  * Default value for nullsym is empty string.
!  */
! Datum
! to_array(PG_FUNCTION_ARGS)
! {
! 	text	   *inputstring = PG_GETARG_TEXT_PP(0);
! 	text	   *fldsep = PG_GETARG_TEXT_PP(1);
! 	text	   *null_string = PG_GETARG_TEXT_PP(2);
! 	bool		isNull;
! 	Datum	result;
! 
! 	result = _text_to_array(fcinfo, inputstring, fldsep, null_string, &isNull);
! 	
! 	if (isNull)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_ARRAYTYPE_P(result);
! }
! 
! /*
!  * common code for array_to_text and to_string function. null_string can be
!  * NULL (only for array_to_text function).
!  */
! static text *
! _array_to_text(FunctionCallInfo fcinfo, ArrayType *v, char *fldsep, char *null_string)
  {
  	int			nitems,
  			   *dims,
  				ndims;
***************
*** 3092,3098 ****
  
  	/* if there are no elements, return an empty string */
  	if (nitems == 0)
! 		PG_RETURN_TEXT_P(cstring_to_text(""));
  
  	element_type = ARR_ELEMTYPE(v);
  	initStringInfo(&buf);
--- 3145,3151 ----
  
  	/* if there are no elements, return an empty string */
  	if (nitems == 0)
! 		return cstring_to_text("");
  
  	element_type = ARR_ELEMTYPE(v);
  	initStringInfo(&buf);
***************
*** 3140,3146 ****
  		/* Get source element, checking for NULL */
  		if (bitmap && (*bitmap & bitmask) == 0)
  		{
! 			/* we ignore nulls */
  		}
  		else
  		{
--- 3193,3207 ----
  		/* Get source element, checking for NULL */
  		if (bitmap && (*bitmap & bitmask) == 0)
  		{
! 			/* we ignore nulls, when null_string isn't defined (is NULL) */
! 			if (null_string != NULL)
! 			{
! 				if (printed)
! 					appendStringInfo(&buf, "%s%s", fldsep, null_string);
! 				else
! 					appendStringInfoString(&buf, null_string);
! 				printed = true;
! 			}
  		}
  		else
  		{
***************
*** 3170,3178 ****
  		}
  	}
  
! 	PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
  }
  
  #define HEXBASE 16
  /*
   * Convert a int32 to a string containing a base 16 (hex) representation of
--- 3231,3269 ----
  		}
  	}
  
! 	return cstring_to_text_with_len(buf.data, buf.len);
  }
  
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+ 	char	   *fldsep = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ 	
+ 	PG_RETURN_TEXT_P(_array_to_text(fcinfo, v, fldsep, NULL));
+ }
+ 
+ /*
+  * to_string
+  * concatenate Cstring representation of input array elements
+  * using provided field separator and null string
+  */
+ Datum
+ to_string(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+ 	char	   *fldsep = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ 	char	   *null_string = text_to_cstring(PG_GETARG_TEXT_PP(2));
+ 	
+ 	PG_RETURN_TEXT_P(_array_to_text(fcinfo, v, fldsep, null_string));
+ }
+ 
+ 
  #define HEXBASE 16
  /*
   * Convert a int32 to a string containing a base 16 (hex) representation of
*** ./src/include/catalog/pg_proc.h.orig	2010-02-26 03:01:21.000000000 +0100
--- ./src/include/catalog/pg_proc.h	2010-05-05 19:41:37.518244240 +0200
***************
*** 1022,1027 ****
--- 1022,1031 ----
  DESCR("split delimited text into text[]");
  DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 25 "2277 25" _null_ _null_ _null_ _null_ array_to_text _null_ _null_ _null_ ));
  DESCR("concatenate array elements, using delimiter, into text");
+ DATA(insert OID = 950 (  to_array   PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1009 "25 25 25" _null_ _null_ _null_ _null_ to_array _null_ _null_ _null_ ));
+ DESCR("split delimited text into text[], possible to set null string");
+ DATA(insert OID = 951 (  to_string   PGNSP PGUID 12 1 0 0 f f f t f i 3 0 25 "2277 25 25" _null_ _null_ _null_ _null_ to_string _null_ _null_ _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text, possible to set null string");
  DATA(insert OID = 515 (  array_larger	   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_larger _null_ _null_ _null_ ));
  DESCR("larger of two");
  DATA(insert OID = 516 (  array_smaller	   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_smaller _null_ _null_ _null_ ));
*** ./src/include/utils/array.h.orig	2010-01-02 17:58:09.000000000 +0100
--- ./src/include/utils/array.h	2010-05-05 18:02:44.191119209 +0200
***************
*** 274,279 ****
--- 274,280 ----
  extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
  					   Oid element_type,
  					   Datum element,
+ 					   bool isNull,
  					   int ndims);
  
  extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
*** ./src/include/utils/builtins.h.orig	2010-02-26 03:01:28.000000000 +0100
--- ./src/include/utils/builtins.h	2010-05-05 18:36:03.468243638 +0200
***************
*** 714,719 ****
--- 714,721 ----
  extern Datum split_text(PG_FUNCTION_ARGS);
  extern Datum text_to_array(PG_FUNCTION_ARGS);
  extern Datum array_to_text(PG_FUNCTION_ARGS);
+ extern Datum to_array(PG_FUNCTION_ARGS);
+ extern Datum to_string(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
*** ./src/test/regress/expected/arrays.out.orig	2010-02-18 19:41:47.000000000 +0100
--- ./src/test/regress/expected/arrays.out	2010-05-05 20:19:26.000000000 +0200
***************
*** 1208,1210 ****
--- 1208,1265 ----
   [5:5]={"(42,43)"}
  (1 row)
  
+ -- check to_string and to_array functions
+ select to_array('abc','');
+  to_array 
+ ----------
+  {abc}
+ (1 row)
+ 
+ select to_array('abc','','abc');
+  to_array 
+ ----------
+  {NULL}
+ (1 row)
+ 
+ select to_array('abc',',');
+  to_array 
+ ----------
+  {abc}
+ (1 row)
+ 
+ select to_array('abc',',','abc');
+  to_array 
+ ----------
+  {NULL}
+ (1 row)
+ 
+ select to_array('1,2,3,4,,6',',');
+      to_array     
+ ------------------
+  {1,2,3,4,NULL,6}
+ (1 row)
+ 
+ select to_array('1,2,3,4,,6',',','');
+      to_array     
+ ------------------
+  {1,2,3,4,NULL,6}
+ (1 row)
+ 
+ select to_array('1,2,3,4,*,6',',','*');
+      to_array     
+ ------------------
+  {1,2,3,4,NULL,6}
+ (1 row)
+ 
+ select to_string(array[1,2,3,4,NULL,6],',');
+  to_string  
+ ------------
+  1,2,3,4,,6
+ (1 row)
+ 
+ select to_string(array[1,2,3,4,NULL,6],',','*');
+   to_string  
+ -------------
+  1,2,3,4,*,6
+ (1 row)
+ 
*** ./src/test/regress/sql/arrays.sql.orig	2010-02-18 19:41:47.000000000 +0100
--- ./src/test/regress/sql/arrays.sql	2010-05-05 20:13:12.194118475 +0200
***************
*** 412,414 ****
--- 412,426 ----
  select * from t1;
  update t1 set f1[5].q2 = 43;
  select * from t1;
+ 
+ -- check to_string and to_array functions
+ 
+ select to_array('abc','');
+ select to_array('abc','','abc');
+ select to_array('abc',',');
+ select to_array('abc',',','abc');
+ select to_array('1,2,3,4,,6',',');
+ select to_array('1,2,3,4,,6',',','');
+ select to_array('1,2,3,4,*,6',',','*');
+ select to_string(array[1,2,3,4,NULL,6],',');
+ select to_string(array[1,2,3,4,NULL,6],',','*');