diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 70dab53..2256d9c 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -9544,6 +9544,12 @@ SELECT NULLIF(value, '(none)') ...
string_to_array
+ to_array
+
+
+ to_string
+
+
unnest
@@ -9686,6 +9692,28 @@ SELECT NULLIF(value, '(none)') ...
+ to_array(text, text , text)
+
+
+ text[]
+ splits string into array elements using supplied delimiter and null string (defaults to the empty string)
+ to_array('1,2,3,,5', ',')
+ {1,2,3,4,NULL,5}
+
+
+
+
+ to_string(anyarray, text , text)
+
+
+ text
+ concatenates array elements using supplied delimiter and null string (defaults to the empty string)
+ to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*')
+ 1,2,3,*,5
+
+
+
+
unnest(anyarray)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 8852326..24a79e8 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -487,3 +487,11 @@ COMMENT ON FUNCTION ts_debug(text) IS
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';
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 00e9c54..7f88b82 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -407,9 +407,11 @@ ArrayType *
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,6 +431,7 @@ create_singleton_array(FunctionCallInfo fcinfo,
ndims, MAXDIM)));
dvalues[0] = element;
+ nulls[0] = isNull;
for (i = 0; i < ndims; i++)
{
@@ -462,7 +465,7 @@ create_singleton_array(FunctionCallInfo fcinfo,
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
- return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
+ return construct_md_array(dvalues, nulls, ndims, dims, lbs, element_type,
typlen, typbyval, typalign);
}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index af28c15..676fdc1 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2965,16 +2965,22 @@ split_text(PG_FUNCTION_ARGS)
}
/*
- * text_to_array
- * parse input string
- * return text array of elements
- * based on provided field separator
+ * Return true when two text params are equal.
*/
-Datum
-text_to_array(PG_FUNCTION_ARGS)
+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)
{
- text *inputstring = PG_GETARG_TEXT_PP(0);
- text *fldsep = PG_GETARG_TEXT_PP(1);
int inputstring_len;
int fldsep_len;
TextPositionState state;
@@ -2985,9 +2991,10 @@ text_to_array(PG_FUNCTION_ARGS)
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,7 +3006,8 @@ text_to_array(PG_FUNCTION_ARGS)
if (inputstring_len < 1)
{
text_position_cleanup(&state);
- PG_RETURN_NULL();
+ *isNull = true;
+ return (Datum) 0;
}
/*
@@ -3009,8 +3017,10 @@ text_to_array(PG_FUNCTION_ARGS)
if (fldsep_len < 1)
{
text_position_cleanup(&state);
- PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
- PointerGetDatum(inputstring), 1));
+ 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,11 +3046,12 @@ text_to_array(PG_FUNCTION_ARGS)
/* 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),
- false,
+ is_null_string,
TEXTOID,
CurrentMemoryContext);
@@ -3057,20 +3068,65 @@ text_to_array(PG_FUNCTION_ARGS)
text_position_cleanup(&state);
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
- CurrentMemoryContext));
+ *isNull = false;
+ return makeArrayResult(astate, CurrentMemoryContext);
}
/*
- * array_to_text
- * concatenate Cstring representation of input array elements
- * using provided field separator
+ * text_to_array
+ * parse input string
+ * return text array of elements
+ * based on provided field separator
*/
Datum
-array_to_text(PG_FUNCTION_ARGS)
+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 a string delimited by fldsep, and return an array. Any instances of
+ * null_string in the inputstring are replaced by NULL elements in the result
+ * array.
+ *
+ * The default value for null_string is the 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 functions. 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)
{
- ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
- char *fldsep = text_to_cstring(PG_GETARG_TEXT_PP(1));
int nitems,
*dims,
ndims;
@@ -3092,7 +3148,7 @@ array_to_text(PG_FUNCTION_ARGS)
/* if there are no elements, return an empty string */
if (nitems == 0)
- PG_RETURN_TEXT_P(cstring_to_text(""));
+ return cstring_to_text("");
element_type = ARR_ELEMTYPE(v);
initStringInfo(&buf);
@@ -3140,7 +3196,15 @@ array_to_text(PG_FUNCTION_ARGS)
/* Get source element, checking for NULL */
if (bitmap && (*bitmap & bitmask) == 0)
{
- /* we ignore nulls */
+ /* 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,9 +3234,41 @@ array_to_text(PG_FUNCTION_ARGS)
}
}
- PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
+ 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 the
+ * provided field separator. NULL elements in the input array are rendered
+ * using the provided null_string, which defaults to the empty 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
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6036493..999b331 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1022,6 +1022,10 @@ DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 1 0 0 f f f t f i 2 0
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_ ));
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 1376f8c..db9a615 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -274,6 +274,7 @@ extern Datum array_cat(PG_FUNCTION_ARGS);
extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
Oid element_type,
Datum element,
+ bool isNull,
int ndims);
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 45123fd..6c539e1 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -715,6 +715,8 @@ extern text *replace_text_regexp(text *src_text, void *regexp,
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);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 3ab18be..df9bec6 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1208,3 +1208,58 @@ select * from t1;
[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)
+
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index f65bc45..dd149bb 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -412,3 +412,15 @@ insert into t1 (f1[5].q1) values(42);
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],',','*');