Hello I am sending a actualised patch. There is only one significant change to last patch. Function to_string was renamed to "implode" and to_array was renamed "explode".
Regards Pavel Stehule
*** ./doc/src/sgml/func.sgml.orig 2010-07-23 21:18:04.698690857 +0200 --- ./doc/src/sgml/func.sgml 2010-07-23 21:51:08.860689007 +0200 *************** *** 4652,4658 **** </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 --- 4652,4658 ---- </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 *************** *** 9541,9546 **** --- 9541,9552 ---- <primary>array_upper</primary> </indexterm> <indexterm> + <primary>explode</primary> + </indexterm> + <indexterm> + <primary>implode</primary> + </indexterm> + <indexterm> <primary>string_to_array</primary> </indexterm> <indexterm> *************** *** 9675,9680 **** --- 9681,9708 ---- <row> <entry> <literal> + <function>explode</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>exolode('1,2,3,,5', ',')</literal></entry> + <entry><literal>{1,2,3,4,NULL,5}</literal></entry> + </row> + <row> + <entry> + <literal> + <function>implode</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>implode(ARRAY[1, 2, 3, NULL, 5], ',', '*')</literal></entry> + <entry><literal>1,2,3,*,5</literal></entry> + </row> + <row> + <entry> + <literal> <function>string_to_array</function>(<type>text</type>, <type>text</type>) </literal> </entry> *** ./src/backend/catalog/system_views.sql.orig 2010-07-23 21:18:04.806852641 +0200 --- ./src/backend/catalog/system_views.sql 2010-07-23 22:03:56.329687977 +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 + implode(v anyarray, fldsep text, null_string text DEFAULT '') + RETURNS text STABLE LANGUAGE internal AS 'implode'; + + CREATE OR REPLACE FUNCTION + explode(inputstr text, fldsep text, null_string text DEFAULT '') + RETURNS text[] IMMUTABLE LANGUAGE internal AS 'explode'; *** ./src/backend/utils/adt/array_userfuncs.c.orig 2010-07-23 21:18:04.880689496 +0200 --- ./src/backend/utils/adt/array_userfuncs.c 2010-07-23 21:18:36.467693435 +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-07-23 21:18:04.911693316 +0200 --- ./src/backend/utils/adt/varlena.c 2010-07-23 22:00:47.832690000 +0200 *************** *** 75,80 **** --- 75,83 ---- static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl); static StringInfo makeStringAggState(FunctionCallInfo fcinfo); + static Datum _explode(PG_FUNCTION_ARGS); + static Datum _implode(PG_FUNCTION_ARGS); + /***************************************************************************** * CONVERSION ROUTINES EXPORTED FOR USE BY C CODE * *************** *** 2965,2970 **** --- 2968,2984 ---- } /* + * Returns true when two text params are same. + */ + static + bool text_isequal(text *txt1, text *txt2) + { + return DatumGetBool(DirectFunctionCall2(texteq, + PointerGetDatum(txt1), + PointerGetDatum(txt2))); + } + + /* * text_to_array * parse input string * return text array of elements *************** *** 2973,3076 **** 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; - int fldnum; - int start_posn; - int end_posn; - int chunk_len; 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. ! */ ! inputstring_len = state.len1; ! fldsep_len = state.len2; ! ! /* return NULL for empty input string */ ! if (inputstring_len < 1) ! { ! text_position_cleanup(&state); PG_RETURN_NULL(); ! } ! /* ! * empty field separator return one element, 1D, array using the input ! * string */ ! if (fldsep_len < 1) ! { ! text_position_cleanup(&state); ! PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, ! PointerGetDatum(inputstring), 1)); ! } ! ! start_posn = 1; ! /* start_ptr points to the start_posn'th character of inputstring */ ! start_ptr = VARDATA_ANY(inputstring); ! ! for (fldnum = 1;; fldnum++) /* field number is 1 based */ { ! CHECK_FOR_INTERRUPTS(); ! ! end_posn = text_position_next(start_posn, &state); ! if (end_posn == 0) { ! /* fetch last field */ ! chunk_len = ((char *) inputstring + VARSIZE_ANY(inputstring)) - start_ptr; } ! else { ! /* fetch non-last field */ ! chunk_len = charlen_to_bytelen(start_ptr, end_posn - start_posn); } ! /* 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); ! pfree(result_text); ! if (end_posn == 0) ! break; ! start_posn = end_posn; ! start_ptr += chunk_len; ! start_posn += fldsep_len; ! start_ptr += charlen_to_bytelen(start_ptr, fldsep_len); } ! 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; --- 2987,3197 ---- Datum text_to_array(PG_FUNCTION_ARGS) { ! text *inputstr = PG_GETARG_TEXT_PP(0); ! ! /* function string_to_array returns a NULL for empty string */ ! if (VARSIZE_ANY_EXHDR(inputstr) < 1) ! PG_RETURN_NULL(); ! ! return _explode(fcinfo); ! } ! ! /* ! * just only outer wrapper ! */ ! Datum ! explode(PG_FUNCTION_ARGS) ! { ! return _explode(fcinfo); ! } ! ! /* ! * _to_array - common code for to_array and text_to_array functions ! */ ! static Datum ! _explode(PG_FUNCTION_ARGS) ! { int inputstring_len; char *start_ptr; text *result_text; ArrayBuildState *astate = NULL; + bool is_null; + text *inputstring; + text *fldsep; + text *null_string; ! /* when input string is NULL, then result is NULL too */ ! if (PG_ARGISNULL(0)) PG_RETURN_NULL(); ! ! inputstring = PG_GETARG_TEXT_PP(0); ! /* ! * when null string is NULL, there are not string representation for NULL, ! * the behave is compatible old text_to_array function. to_array function has ! * three arguments - last argument has default value. text_to_array function ! * has two arguments. When null_string is NULL, then behave is same like ! * old text_to_array (string_to_array) function. */ ! if (PG_NARGS() > 2) ! null_string = (!PG_ARGISNULL(2)) ? PG_GETARG_TEXT_PP(2) : NULL; ! else ! null_string = NULL; ! ! /* ! * when separator is NULL, then returns every character as field, ! * there are no any character used as separator - etc separator is ! * space between chars. ! */ ! if (!PG_ARGISNULL(1)) { ! TextPositionState state; ! int fldnum; ! int start_posn; ! int end_posn; ! int chunk_len; ! ! fldsep = PG_GETARG_TEXT_PP(1); ! ! text_position_setup(inputstring, fldsep, &state); ! /* return empty array for empty input string */ ! if (state.len1 < 1) { ! text_position_cleanup(&state); ! PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID)); } ! ! /* ! * empty field separator return one element, 1D, array using the input ! * string ! */ ! if (state.len2 < 1) { ! text_position_cleanup(&state); ! /* single element can be a NULL too */ ! is_null = (null_string != NULL) ? text_isequal(null_string, inputstring) : false; ! PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, ! PointerGetDatum(inputstring), ! is_null, 1)); } + + start_posn = 1; + /* start_ptr points to the start_posn'th character of inputstring */ + start_ptr = VARDATA_ANY(inputstring); + + for (fldnum = 1;; fldnum++) /* field number is 1 based */ + { + CHECK_FOR_INTERRUPTS(); ! end_posn = text_position_next(start_posn, &state); ! ! if (end_posn == 0) ! { ! /* fetch last field */ ! chunk_len = ((char *) inputstring + VARSIZE_ANY(inputstring)) - start_ptr; ! } ! else ! { ! /* fetch non-last field */ ! chunk_len = charlen_to_bytelen(start_ptr, end_posn - start_posn); ! } ! /* must build a temp text datum to pass to accumArrayResult */ ! result_text = cstring_to_text_with_len(start_ptr, chunk_len); ! is_null = (null_string != NULL) ? text_isequal(null_string, result_text) : false; ! ! /* stash away this field */ ! astate = accumArrayResult(astate, PointerGetDatum(result_text), ! is_null, ! TEXTOID, ! CurrentMemoryContext); ! pfree(result_text); ! if (end_posn == 0) ! break; ! start_posn = end_posn; ! start_ptr += chunk_len; ! start_posn += state.len2; ! start_ptr += charlen_to_bytelen(start_ptr, state.len2); ! } ! ! text_position_cleanup(&state); } + else + { + /* + * simple mode + * when field separator is NULL, then divide chars from string. + */ + inputstring_len = VARSIZE_ANY_EXHDR(inputstring); + + /* returns empty array for empty string */ + if (inputstring_len < 1) + PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID)); + + start_ptr = VARDATA_ANY(inputstring); + + while (inputstring_len > 0) + { + int chunk_len; + + chunk_len = pg_mblen(start_ptr); + result_text = cstring_to_text_with_len(start_ptr, chunk_len); + + is_null = (null_string != NULL) ? text_isequal(null_string, result_text) : false; + + /* stash away this field */ + astate = accumArrayResult(astate, + PointerGetDatum(result_text), + is_null, + TEXTOID, + CurrentMemoryContext); ! pfree(result_text); ! start_ptr += chunk_len; ! inputstring_len -= chunk_len; ! } ! } ! PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext)); } /* * array_to_text * concatenate Cstring representation of input array elements ! * using provided field separator - just only wrapper for ! * strict version of function. */ Datum array_to_text(PG_FUNCTION_ARGS) { ! return _implode(fcinfo); ! } ! ! /* ! * implode ! * concatenate Cstring representation of input array elements ! * using provided field separator and null string - just only ! * wrapper for non strict three params function. ! */ ! Datum ! implode(PG_FUNCTION_ARGS) ! { ! return _implode(fcinfo); ! } ! ! /* ! * common code for array_to_text and to_string function. null_string can be ! * NULL (only for array_to_text function). ! */ ! static Datum ! _implode(PG_FUNCTION_ARGS) ! { ! ArrayType *v; int nitems, *dims, ndims; *************** *** 3085,3090 **** --- 3206,3241 ---- int bitmask; int i; ArrayMetaState *my_extra; + char *fieldsep; + char *null_string; + text *result; + + + /* returns NULL when input parameter is NULL */ + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + v = PG_GETARG_ARRAYTYPE_P(0); + + + /* field separator cannot be a NULL */ + if (PG_ARGISNULL(1)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("field separator cannot be NULL"))); + + fieldsep = text_to_cstring(PG_GETARG_TEXT_PP(1)); + + /* get a NULL string */ + if (PG_NARGS() > 2) + { + if (!PG_ARGISNULL(2)) + null_string = text_to_cstring(PG_GETARG_TEXT_PP(2)); + else + null_string = NULL; + } + else + null_string = NULL; ndims = ARR_NDIM(v); dims = ARR_DIMS(v); *************** *** 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); --- 3243,3249 ---- /* if there are no elements, return an empty string */ if (nitems == 0) ! PG_RETURN_TEXT_P(cstring_to_text_with_len("", 0)); element_type = ARR_ELEMTYPE(v); initStringInfo(&buf); *************** *** 3140,3146 **** /* Get source element, checking for NULL */ if (bitmap && (*bitmap & bitmask) == 0) { ! /* we ignore nulls */ } else { --- 3291,3305 ---- /* 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", fieldsep, null_string); ! else ! appendStringInfoString(&buf, null_string); ! printed = true; ! } } else { *************** *** 3149,3155 **** value = OutputFunctionCall(&my_extra->proc, itemvalue); if (printed) ! appendStringInfo(&buf, "%s%s", fldsep, value); else appendStringInfoString(&buf, value); printed = true; --- 3308,3314 ---- value = OutputFunctionCall(&my_extra->proc, itemvalue); if (printed) ! appendStringInfo(&buf, "%s%s", fieldsep, value); else appendStringInfoString(&buf, value); printed = true; *************** *** 3169,3176 **** } } } ! PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len)); } #define HEXBASE 16 --- 3328,3338 ---- } } } + + result = cstring_to_text_with_len(buf.data, buf.len); + pfree(buf.data); ! PG_RETURN_TEXT_P(result); } #define HEXBASE 16 *** ./src/include/catalog/pg_proc.h.orig 2010-07-23 21:18:04.970977412 +0200 --- ./src/include/catalog/pg_proc.h 2010-07-23 21:53:09.567690848 +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 ( explode PGNSP PGUID 12 1 0 0 f f f f f i 3 0 1009 "25 25 25" _null_ _null_ _null_ _null_ explode _null_ _null_ _null_ )); + DESCR("split delimited text into text[], possible to set null string"); + DATA(insert OID = 951 ( implode PGNSP PGUID 12 1 0 0 f f f f f i 3 0 25 "2277 25 25" _null_ _null_ _null_ _null_ implode _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-07-23 21:18:05.025427635 +0200 --- ./src/include/utils/array.h 2010-07-23 21:18:36.532691416 +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-07-22 03:22:35.000000000 +0200 --- ./src/include/utils/builtins.h 2010-07-23 21:52:27.718688550 +0200 *************** *** 716,721 **** --- 716,723 ---- 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 exlode(PG_FUNCTION_ARGS); + extern Datum implode(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-07-23 21:18:05.106040250 +0200 --- ./src/test/regress/expected/arrays.out 2010-07-23 22:06:16.000000000 +0200 *************** *** 1208,1210 **** --- 1208,1297 ---- [5:5]={"(42,43)"} (1 row) + -- check to_string and to_array functions + select explode('abc',''); + explode + --------- + {abc} + (1 row) + + select explode('abc','','abc'); + explode + --------- + {NULL} + (1 row) + + select explode('abc',','); + explode + --------- + {abc} + (1 row) + + select explode('abc',',','abc'); + explode + --------- + {NULL} + (1 row) + + select explode('1,2,3,4,,6',','); + explode + ------------------ + {1,2,3,4,NULL,6} + (1 row) + + select explode('1,2,3,4,,6',',',''); + explode + ------------------ + {1,2,3,4,NULL,6} + (1 row) + + select explode('1,2,3,4,*,6',',','*'); + explode + ------------------ + {1,2,3,4,NULL,6} + (1 row) + + select explode(NULL, ',') is NULL; + ?column? + ---------- + t + (1 row) + + select explode('', ','); + explode + --------- + {} + (1 row) + + select implode(NULL::int4[], ',') is NULL; + ?column? + ---------- + t + (1 row) + + select implode('{}'::int4[], ','); + implode + --------- + + (1 row) + + select implode(array[1,2,3,4,NULL,6],','); + implode + ------------ + 1,2,3,4,,6 + (1 row) + + select implode(array[1,2,3,4,NULL,6],',','*'); + implode + ------------- + 1,2,3,4,*,6 + (1 row) + + select implode(array[1,2,3,4,NULL,6],NULL); -- should fail + ERROR: field separator cannot be NULL + select implode(array[1,2,3,4,NULL,6],',', NULL); -- ignore NULL value + implode + ----------- + 1,2,3,4,6 + (1 row) + *** ./src/test/regress/sql/arrays.sql.orig 2010-07-23 21:18:05.123458199 +0200 --- ./src/test/regress/sql/arrays.sql 2010-07-23 21:55:46.646687842 +0200 *************** *** 412,414 **** --- 412,432 ---- select * from t1; update t1 set f1[5].q2 = 43; select * from t1; + + -- check to_string and to_array functions + + select explode('abc',''); + select explode('abc','','abc'); + select explode('abc',','); + select explode('abc',',','abc'); + select explode('1,2,3,4,,6',','); + select explode('1,2,3,4,,6',',',''); + select explode('1,2,3,4,*,6',',','*'); + select explode(NULL, ',') is NULL; + select explode('', ','); + select implode(NULL::int4[], ',') is NULL; + select implode('{}'::int4[], ','); + select implode(array[1,2,3,4,NULL,6],','); + select implode(array[1,2,3,4,NULL,6],',','*'); + select implode(array[1,2,3,4,NULL,6],NULL); -- should fail + select implode(array[1,2,3,4,NULL,6],',', NULL); -- ignore NULL value
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers