2201B ERROR: invalid regular expression when using replace() function - postgresql

Why Postgres gives me this error
[2014-10-30 13:43:36] [2201B] ERROR: invalid regular expression:
invalid escape \ sequence Where: PL/pgSQL function
normalize_username() line 5 at assignment
when I use the SQL statement:
UPDATE users."user" SET username = username
with the following trigger active:
CREATE OR REPLACE FUNCTION normalize_username()
RETURNS TRIGGER AS $normalize_username$
BEGIN
IF NOT (NEW.username IS NULL)
THEN
NEW.username := replace(NEW.username, 'ё', 'е');
END IF;
RETURN NEW;
END;
$normalize_username$ LANGUAGE plpgsql;
Table was created like this:
CREATE TABLE USERS.user (
user_id SERIAL PRIMARY KEY,
username CITEXT,
)
replace() is a simple text function, right? It shouldn't have any relation to regular expressions I suppose.

The problem was solved by casting username to TEXT in assignment like this:
CREATE OR REPLACE FUNCTION normalize_username()
RETURNS TRIGGER AS $normalize_username$
BEGIN
IF NOT (NEW.username IS NULL)
THEN
NEW.username := replace(NEW.username::TEXT, 'ё', 'е');
END IF;
RETURN NEW;
END;
$normalize_username$ LANGUAGE plpgsql;
It looks like CITEXT column operations are silently converted to regular expression operations under the hood. Can someone confirm this?

I faced same issue with postgres v13 function REPLACE(source, old_text, new_text ). Casting old_text to TEXT resolved the issue.

Related

Facing issue while calling a stored procedure with if else condition in redshift

I created the stored procedure in redshift. Below is the code for that.
create or replace procedure sp4(f1 IN int)
as
$$
begin
IF f1==0 then
CREATE TABLE myetl(a int, b varchar);
ELSE
insert into new_tbl(id) values(47);
END IF;
end;
$$ LANGUAGE plpgsql;
While calling the stored procedure I am getting the error like this
call sp4(0)
ERROR: operator does not exist: integer == integer Hint: No operator matches the given name and argument type(s). You may need to add explicit type casts. Where: SQL statement "SELECT $1 ==0" PL/pgSQL function "sp4" line 2 at if
Your comparison should be IF f0 = 0 THEN, with single equal signs. See the Redshift PLpgSQL documentation

postgresql function and trigger execute select current_setting into integer variable causes error

Postgresql 9.6.x
I am getting an error with a postgresql function where I am recording a log on every table modification. This was all working great until I added this functionality where I am recording the current user id using current_setting functionality of postgresql. I set the current user on transactions in the background like so:
select set_config('myvars.active_user_id', '2123', true)
All this functionality works perfectly fine, except when the user is not set. This occurs when the tables are being updated by back end system queries and in that case the setting 'myvars.active_user_id' is null.
I want it to be null when it is not set. The user id field in the log is nullable.
It seems to be trying to convert null to an empty string and put that in the integer variable which it doesn't like.
This appears to be some kind of weird problem specific to functions with triggers. I must be doing something wrong because as far as I know assigning a null value through a select...into"is no issue.
The error I get in that case is so:
PSQLException: ERROR: invalid input syntax for integer: ""
I have added tracing statements and it is on this line:
EXECUTE 'select current_setting(''myvars.active_user_id'', true) ' into log_user_id;
I don't understand why in this setting it gets upset about the null value. But it seems limited to this type of trigger function. Below is essentially the function I am using
CREATE OR REPLACE FUNCTION update_log() RETURNS TRIGGER AS $update_log$
DECLARE
logid int;
log_user_id int;
BEGIN
EXECUTE 'select current_setting(''myvars.active_user_id'', true) ' into log_user_id;
IF (TG_OP='DELETE') THEN
EXECUTE 'select nextval(''seq_log'') ' into logid;
-- INSERT INTO log ....
RETURN NULL;
ELSIF (TG_OP='INSERT') THEN
EXECUTE 'select nextval(''seq_log'') ' into logid;
-- INSERT INTO log ....
RETURN NEW;
ELSIF (TG_OP='UPDATE') THEN
-- INSERT INTO log ....
END IF;
END IF;
RETURN NULL;
END;
$log$ LANGUAGE plpgsql;
Any thoughts?
GUC (Global User Setting) variables like your myvars.active_user_id are not nullable internally. It holds text or empty text. These variables cannot to store NULL. So when you store NULL, then empty string is stored, and this empty string is returned from function current_setting.
In Postgres (and any database without Oracle) NULL is not empty string and empty string is not NULL.
So this error is expected:
postgres=# do $$
declare x int;
begin
perform set_config('x.xx', null, false);
execute $_$ select current_setting('x.xx', true) $_$ into x;
end;
$$;
ERROR: invalid input syntax for integer: ""
CONTEXT: PL/pgSQL function inline_code_block line 5 at EXECUTE
You need to check result first, and replace empty string by NULL:
create or replace function nullable(anyelement)
returns anyelement as $$
select case when $1 = '' then NULL else $1 end;
$$ language sql;
do $$
declare x int;
begin
perform set_config('x.xx', null, false);
execute $_$ select nullable(current_setting('x.xx', true)) $_$ into x;
end;
$$;
DO
#Laurenz Albe has big true in your comment. Use dynamic SQL (execute command) only when it is necessary. It is not this case. So your code should looks like:
do $$
declare x int;
begin
perform set_config('x.xx', null, false);
x := nullable(current_setting('x.xx', true));
end;
$$;
DO
Note: There is buildin function nullif, so your code can looks like (and sure, buildin functionality should be preferred):
do $$
declare x int;
begin
perform set_config('x.xx', null, false);
x := nullif(current_setting('x.xx', true), '');
end;
$$;
DO

PostgreSQL - not a known variable in procedure

I'm having trouble with writing a procedure in PostgreSQL. I can create the procedure, yet when i try to execute i get an error.The error i get
ERROR: "v_all_atts" is not a known variable
the query is:
CREATE OR REPLACE FUNCTION rebuild_views_with_extra_atts()
RETURNS VOID AS $$
DECLARE
v_all_atts varchar(4000);
BEGIN
CREATE OR REPLACE FUNCTION add_column(p_table text, p_column text,p_category text) RETURNS VOID AS $nothing$
declare
v_column_exists bigint := false ;
BEGIN
SELECT
string_agg( CASE WHEN owner='alarm' THEN 'ai' WHEN owner='fault' THEN 'fi'
END ||'.'||lower(alias) , ', ' ORDER BY owner, alias) AS string
INTO STRICT
v_all_atts
FROM
extra_attribute_cfg
WHERE
owner NOT LIKE 'virtual' and enable = true and v_column_exists = true;
IF LENGTH(v_all_atts) is not null THEN
v_all_atts := ', '||v_all_atts;
END IF;
v_view:= q'#
CREATE OR REPLACE VIEW alarm_view AS
SELECT
fi.fault_id, ai.alarm_id,
#'||v_all_atts||q'#
FROM
alarm ai
INNER JOIN fault fi
ON fi.fault_id = ai.fault_id
#';
EXECUTE v_view;
END;
$nothing$ language plpgsql;
end;
$$ LANGUAGE plpgsql;
I have taken a long look at Postgres documentation and cannot find what is wrong and didn't find any answer to this specific situation
Your rebuild_views_with_extra_atts() function is creating the add_column() function.
add_column() uses the v_all_atts variable, but it doesn't exist in that function, it exists only in the rebuild_views_with_extra_atts() function.
To resolve this, it really depends on what you're trying to do. If that variable should exist in the add_column() function, then declare it in there. If you're trying to use the value of v_all_atts when creating add_column() (e.g. so that the content of the function's body is dependent on the value of that variable), then you really need to use dynamic sql to generate a TEXT version of the CREATE OR REPLACE ... code, then EXECUTE it.

Returning from a function with OUT parameter

I have an error, but I don't know what the problem is.
I want execute a function and return a value from a column filled in by the column default, a sequence - the equivalent of currval(sequence).
I use:
PostgreSQL 9.0
pgAdmin III
CREATE OR REPLACE FUNCTION name_function(in param_1 character varying
, out param_2 bigint)
AS
$$
BEGIN
INSERT INTO table (collumn_seq,param_1) VALUES (DEFAULT,param_1)
returning collumn_seq;
--where:collumn_seq reference a collumn serial..
END;
$$
LANGUAGE plpgsql VOLATILE;
I can create the function without error but when trying to execute, the following error is returned:
SELECT name_function('GHGHGH');
ERROR: The query has no destination for result data
It would work like this:
CREATE OR REPLACE FUNCTION name_function(param_1 varchar
, OUT param_2 bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO table (collumn_seq, param_1) -- "param_1" also the column name?
VALUES (DEFAULT, param_1)
RETURNING collumn_seq
INTO param2;
END
$func$;
Normally, you would add a RETURN statement, but with OUT parameters, this is optional.
Refer to the manual for more details:
Returning from a function
Executing a Query with a Single-row Result
The simple case can be covered with a plain SQL function.
And you can omit the target column that shall get its DEFAULT value.
And you can just as well use a RETURNS clause in this case:
CREATE OR REPLACE FUNCTION name_function(param_1 varchar)
RETURNS bigint
LANGUAGE sql AS
$func$
INSERT INTO table (param_1) -- "param_1" also the column name?
VALUES (param_1)
RETURNING collumn_seq;
$func$;

How to use EXECUTE FORMAT ... USING in postgres function

CREATE OR REPLACE FUNCTION dummytest_insert_trigger()
RETURNS trigger AS
$BODY$
DECLARE
v_partition_name VARCHAR(32);
BEGIN
IF NEW.datetime IS NOT NULL THEN
v_partition_name := 'dummyTest';
EXECUTE format('INSERT INTO %I VALUES ($1,$2)',v_partition_name)using NEW.id,NEW.datetime;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION dummytest_insert_trigger()
OWNER TO postgres;
I'm trying to insert using
insert into dummyTest values(1,'2013-01-01 00:00:00+05:30');
But it's showing error as
ERROR: function format(unknown) does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Context: PL/pgSQL function "dummytest_insert_trigger" line 8 at EXECUTE statement
I'm unable get the error.
Your function could look like this in Postgres 9.0 or later:
CREATE OR REPLACE FUNCTION dummytest_insert_trigger()
RETURNS trigger AS
$func$
DECLARE
v_partition_name text := quote_ident('dummyTest'); -- assign at declaration
BEGIN
IF NEW.datetime IS NOT NULL THEN
EXECUTE
'INSERT INTO ' || v_partition_name || ' VALUES ($1,$2)'
USING NEW.id, NEW.datetime;
END IF;
RETURN NULL; -- You sure about this?
END
$func$ LANGUAGE plpgsql;
About RETURN NULL:
To ignore result in BEFORE TRIGGER of PostgreSQL?
I would advice not to use mixed case identifiers. With format( .. %I ..) or quote_ident(), you'd get a table named "dummyTest", which you'll have to double quote for the rest of its existence. Related:
Are PostgreSQL column names case-sensitive?
Use lower case instead:
quote_ident('dummytest')
There is really no point in using dynamic SQL with EXECUTE as long as you have a static table name. But that's probably just the simplified example?
You need explicit cast to text:
EXECUTE format('INSERT INTO %I VALUES ($1,$2)'::text ,v_partition_name) using NEW.id,NEW.datetime;