Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the PostgreSQL concat_ws function not immutable?

Tags:

postgresql

In PostgreSQL the function concat_ws accepts only character, character varying, and text data types. Why is it not immutable?

An older documentation has the note that the string functions previously accepted other data types converting them to string. In such a scenario it's clear that the function cannot be marked immutable because e.g. date conversion will differ on settings. But now it's only string data types.

I also found this old post which should no longer apply. Could it be a backwards compability reason?

The function not being immutable means I can for example not use it in a generated column.

like image 268
Tom Avatar asked Dec 13 '25 03:12

Tom


2 Answers

concat() and concat_ws() still accept "any" argument type according to the current documentation. This includes dates.

Since datestyle can be changed, and this affects the conversion to string done during the concat, this makes the functions stable but not immutable.

Demo:

postgres=# select concat_ws(' ', '2025-05-01'::date);
 concat_ws  
------------
 2025-05-01
(1 row)

postgres=# set datestyle='SQL';
SET

postgres=# select concat_ws(' ', '2025-05-01'::date);
 concat_ws  
------------
 05/01/2025
(1 row)
like image 152
Bill Karwin Avatar answered Dec 16 '25 03:12

Bill Karwin


An example of a cheat you can use:

CREATE OR REPLACE FUNCTION public.concat_imm(sep varchar, a varchar, b varchar, c varchar)
 RETURNS character varying
 LANGUAGE sql
 IMMUTABLE
AS $function$
select a || sep || b || sep || c;
$function$
;

create table 
   concat_test (fld_1 varchar, 
                fld_2 varchar, 
                fld_3 varchar, 
                concat_fld varchar 
                  generated always as (concat_imm('|', fld_1, fld_2,  fld_3)) stored);
insert into concat_test values ('cat', 'dog', 'fish');
insert into concat_test values ('1', '2', '07/13/25'::date::varchar)
set datestyle='SQL';
insert into concat_test values ('yes', 'no', '07/13/25'::date::varchar);
select * from concat_test ;
fld_1 | fld_2 |   fld_3    |   concat_fld   
-------+-------+------------+----------------
 cat   | dog   | fish       | cat|dog|fish
 1     | 2     | 2025-07-13 | 1|2|2025-07-13 
yes   | no    | 07/13/2025 | yes|no|07/13/2025

Since you are only working with strings create an immutable function that does the same thing as concat_ws. Then use that in the generated always clause of the column in the table. The caveat is that for the date case I show the output will change depending on the datestyle. That is why the native concat_ws fails as it cannot promise consistent results.

like image 30
Adrian Klaver Avatar answered Dec 16 '25 02:12

Adrian Klaver



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!