How to use "RAISE INFO, RAISE LOG, RAISE DEBUG” to track log in PostgreSQL function? - postgresql

CREATE OR REPLACE FUNCTION mover(src text, dst text, cpquery text, conname text, ifbin boolean) returns void as
$$
DECLARE
cnt integer;
dlcnt integer;
del_count integer;
ret text;
BEGIN
SELECT pg_catalog.dblink_copy_open(conname, dst, ifbin) INTO ret ;
RAISE LOG 'dblink_open %',ret;
execute 'SELECT 1 as check FROM ' || src ||' limit 1' into cnt;
IF cnt=0 THEN
PERFORM pg_sleep(2);
END IF;
IF ifbin=true THEN
RAISE DEBUG 'Start to Copy data with binary';
execute 'COPY (' || cpquery || ' ) to function pg_catalog.dblink_copy_write with binary';
RAISE DEBUG 'Finish Copy data';
ELSE
RAISE DEBUG 'Start to Copy data without binary';
execute 'COPY (' || cpquery || ' ) to function pg_catalog.dblink_copy_write';
RAISE DEBUG 'Finish Copy data';
END IF;
execute 'DELETE FROM ' || src;
GET DIAGNOSTICS del_count=ROW_COUNT;
RAISE INFO 'DELETE % rows',del_count;
SELECT pg_catalog.dblink_copy_end() INTO ret;
RAISE LOG 'dblink_end %',ret;
END;
$$
language plpgsql;
As code, I want to put some message into log by using RAISE, but where is the location
of my log file ? and where RAISE DEBUG output?

They can either be output to the Postgres log, reported back to the client, or both. These are controlled by server-side settings, log_min_messages and client_min_messages.
See the following doc for more details:
http://www.postgresql.org/docs/current/static/plpgsql-errors-and-messages.html
http://www.postgresql.org/docs/current/static/runtime-config-logging.html
As #a_horse_with_no_name suggested: These parameters can also be set via the SET command from the client.
It can be set via the SQL: set client_min_messages to 'debug';

Related

What is the equivalent of PL/SQL %ISOPEN in PL/pgSQL?

I'm migrating an Oracle PLSQL SP to be compatible with Postgres plpgsql (version PostgreSQL 13.6 on x86_64-pc-linux-gnu, compiled by x86_64-pc-linux-gnu-gcc (GCC) 7.4.0, 64-bit).
The exception block of the PLSQL SP has the below code:
exception
when others then
if CURR1%isopen then
close SPV_RECON_INFO;
end if;
open CURR1 for execute select sysdate from dual;
END;
How can %isopen be implemented in Postgres?
That is simple. You have to assign a name to the cursor variable, then you can search for that cursor in pg_cursors. If there is a row with that name, the cursor is open.
Here is a self-contained example:
DO
$$DECLARE
c refcursor;
BEGIN
c := 'mycursor';
/* cursor is not open, EXIST returns FALSE */
RAISE NOTICE '%', EXISTS (SELECT 1 FROM pg_cursors WHERE name = 'mycursor');
OPEN c FOR SELECT * FROM pg_class;
/* cursor is open, EXIST returns TRUE */
RAISE NOTICE '%', EXISTS (SELECT 1 FROM pg_cursors WHERE name = 'mycursor');
END;$$;
NOTICE: f
NOTICE: t
If you do not assign a name, PostgreSQL will generate a name (but you don't know what the name is).
PostgreSQL cursors do not support %ISOPEN or %NOTFOUND. To address this problem %ISOPEN can be replaced by a boolean variable declared internally in the procedure and is updated manually when the cursor is opened or closed.
http://wiki.openbravo.com/wiki/PL-SQL_code_rules_to_write_Oracle_and_Postgresql_code
I often found it convenient in cases like this to create a function that emulates Oracle. In his case something like:
create or replace function cursor_isopen(cur text)
returns boolean
language sql
as $$
select exists (select null
from pg_cursors
where name = cur
) ;
$$;
Then your code becomes something like:
exception
when others then
if cursor_isopen(cur_name::text) then
close SPV_RECON_INFO;
end if;
Of course you need to have preset the cursor name as Laurenz Albe has pointed out. Sample test case.
do $$
declare
cur1 cursor for select table_name from information_schema.tables;
cur2 cursor for select table_name from information_schema.tables;
table_name text;
begin
cur1 := 'closed-cursor';
cur2 := 'open-cursor';
open cur2;
if cursor_isopen(cur1::text)
then
fetch cur1 into table_name;
raise notice 'First table name: %', table_name;
close cur1;
else raise notice 'cursor_isopen(''%'') returned %', cur1::text, cursor_isopen(cur1::text);
end if;
if cursor_isopen(cur2::text)
then
fetch cur2 into table_name;
raise notice 'First table name: %', table_name;
close cur2;
else raise notice 'cursor_isopen(''%'') returned %', cur1::text, cursor_isopen(cur1::text);
end if;
end;
$$;
results:
cursor_isopen('closed-cursor') returned f
cursor_isopen('open-cursor') returned t. First table name: task_states

function does not exist error in PostgreSQL

I'm writing procedure in PostgreSQL While writing am getting error for FN_GETCD like
function fn_getcd(integer) does not exist IS THE NEW VALUE
but i have written function for fn_getcd like this
CREATE OR REPLACE FUNCTION public.fn_getcd(
v_entity_num text)
RETURNS timestamp without time zone
LANGUAGE 'plpgsql'
COST 100
STABLE SECURITY DEFINER PARALLEL UNSAFE
AS $BODY$
DECLARE
O_CBD timestamp;
BEGIN
SELECT
TO_DATE(TO_CHAR(clock_timestamp(),'DD-MON-YYYY'),'DD-MON-YYYY')
INTO STRICT
O_CBD
;
RETURN O_CBD;
EXCEPTION
WHEN OTHERS THEN
RETURN O_CBD;
END;
$BODY$;
please help me to solve this error
tocheck :=cast(P_ENTITY_CODE as TEXT);
RAISE NOTICE 'GOT HERE tocheck :% IS THE NEW VALUE',tocheck;
EXECUTE 'INSERT INTO BENMASTHIST SELECT BENMAST_ENTITY_CODE, BENMAST_CUSTOMER_CODE, BENMAST_BEN_CODE,
FN_GETCD(' ||
tocheck || '),' || W_BEN_HIST_SL ||
', BENMAST_NAME, BENMAST_MOBILE_NO,
BENMAST_EMAIL_ID, BENMAST_PHOTO, BENMAST_SOURCE, BENMAST_CR_BY, BENMAST_CR_ON,
BENMAST_MO_BY, BENMAST_MO_ON, BENMAST_AU_BY, BENMAST_AU_ON, TBA_KEY,BENMAST_FROM
FROM BENMAST WHERE BENMAST_ENTITY_CODE =$1 AND BENMAST_CUSTOMER_CODE =$2 AND BENMAST_BEN_CODE =$3'
USING P_ENTITY_CODE, P_CUST_CODE, P_BEN_CODE;
-- RAISE NOTICE 'GOT HERE BENMASTHIST :% IS THE NEW VALUE',BENMASTHIST;
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
P_SUC_FLAG := 'S';
WHEN OTHERS THEN
--- DBMS_OUTPUT.put_line(SQLERRM);
P_SUC_FLAG := 'F';
P_ERROR := SQLERRM;
RAISE NOTICE 'SQLERRM :% IS THE NEW VALUE',SQLERRM;
P_ERROR := 'BENREG013'; --'Error While Inserting Into Benmast';
-- ROLLBACK;
END;
reference: dollar quote: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
Sometimes you don't know how much quote you need.You can first compose the string then do the execute command.
a horse already said that you function can be as simple as return current_date::timestamp;
If you add more quote then you function will work.
DO $$
DECLARE
_vstr text := 'test';
_vtime timestamp;
_sql text;
_sql1 text;
_sql0 text;
BEGIN
_sql := ' select fn_getcd( ' || '''' || _vstr || '''' || ' )';
_sql1 := ' select fn_getcd( ' || $a$ '$a$ || _vstr || $b$' $b$ || ' )';
_sql0 := ' select fn_getcd( ' || _vstr || ' )';
RAISE NOTICE '_sql is %', _sql;
RAISE NOTICE '_sql1 is %', _sql1;
RAISE NOTICE '_sql1 is %', _sql0;
EXECUTE _sql INTO _vtime;
RAISE NOTICE 'v_time is % ', _vtime;
END
$$
LANGUAGE plpgsql;
You string is like _sql0 format. You need use dollar quote or simple put more single quote in it. (_sql or _sql1).

Doesn't find variable when passing query as parameter

I have a function with a static output that works. (Postgres v.10)
This returns the Amount of users per Server.
Here is the code:
CREATE OR REPLACE FUNCTION public.test()
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
DECLARE
var_req TEXT;
var_error text;
rec_key record;
cur_key CURSOR FOR Select s.srv,s.host,s.port from public.connections() s where s.online = true;
BEGIN
open cur_key;
loop
fetch cur_key into rec_key;
EXIT WHEN NOT FOUND;
var_req :=
'Select * from dblink(
''host=' || rec_key.host || '
port=' || rec_key.port || '
user=**
password=**
dbname=mydb'',
''
select '''''|| rec_key.srv ||''''' as srv ,count (*) as total from users '') as (srv varchar,total integer)
';
return query execute var_req;
end loop;
close cur_key;
END
$function$
;
Output =
srv total
rp1 50
sr2 41
xy 100
To be able to use reuse this query i want to move out the sql part so that i can pass it as parameter.
CREATE OR REPLACE FUNCTION public.test2(text)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
DECLARE
var_req TEXT;
var_error text;
rec_key record;
cur_key CURSOR FOR Select s.srv,s.host,s.port from public.connections() s where s.online = true;
BEGIN
open cur_key;
loop
fetch cur_key into rec_key;
EXIT WHEN NOT FOUND;
var_req :=
'Select * from dblink(
''host=' || rec_key.host || '
port=' || rec_key.port || '
user=**
password=**
dbname=**'',
''
' || $1 || '
';
return query execute var_req;
end loop;
close cur_key;
END
$function$
;
Now when i try to make exact the same query with the dynamic function i don't get it to work.
Like this i am pretty close to my goal but instead of using what's inside the rec_key.srv variable it returns '''''|| rec_key.srv ||''''' :(
select * from public.test2('select ''''''''''''|| rec_key.srv ||'''''''''''' as srv ,count (*) as total from users '') as (srv varchar,total integer)') as (srv varchar,total integer)
Output =
srv total
'''''|| rec_key.srv ||''''' 50
'''''|| rec_key.srv ||''''' 41
'''''|| rec_key.srv ||''''' 100
Can someone explain me how i can call what's inside the variable rec_key.srv with the new function?
Boils down to just this: (!)
SELECT s.srv, t.*
FROM public.connections() s
, dblink('host=' || s.host || ' port=' || s.port || ' user=** password=** dbname=**'
, 'SELECT count(*) FROM users') AS t(total integer);
No wrapper function, no dynamic SQL, no cursor.
Just execute dblink() in an implicit CROSS JOIN LATERAL.
I also added srv to the result like you have in your first function. No need to pipe that through dblink.
See:
What is the difference between LATERAL JOIN and a subquery in PostgreSQL?
Passing arbitrary queries is open to SQL injection. Be sure to understand possible implications and only execute it with trusted input. See:
Demonstrate SQL injection in PL/pgSQL
https://bobby-tables.com/

postgresql two phase commit prepare transaction error: transactions can not be started in PL / pgSQL

I would like to do a two phase commit transaction with prepare transaction for PostgreSQL.
Could you help with the error?
I can not understand how to connect to the remote database via dblick with prepare transaction?
create or replace function insert_into_table_a() returns void as $$
declare
trnxprepare text;
trnxcommit text;
trnxrollback text;
trnxid varchar;
begin
select uuid_generate_v4() into trnxid;
select 'prepare transaction ' || ' ''' || trnxid || ' ''' into trnxprepare;
select 'commit prepared ' || ' ''' || trnxid || ' ''' into trnxcommit;
select 'rollback prepared ' || ' ''' || trnxid || ' ''' into trnxrollback;
insert into table_a values ('test');
perform dblink_connect('cn','dbname=test2 user=test2user password=123456');
perform dblink_exec('cn','insert into table_b values (''test 2'');');
perform dblink_disconnect('cn');
execute trnxprepare;
execute trnxcommit;
exception
when others then
execute trnxrollback;
perform dblink_disconnect('cn');
raise notice '% %', sqlerrm, sqlstate;
end;
$$ language plpgsql;
select insert_into_table_a();
ERROR: ERROR: transactions can not be started in PL / pgSQL
HINT: Use the BEGIN block with the EXCEPTION clause instead.
CONTEXT: insert_into_table_a () PL / pgSQL function, line 24, in EXECUTE
SQL state: 0A000
So, in Postgres, you can't control transactions from inside functions for the most part. You can raise errors to abort them indirectly, if they aren't caught, but you can't begin or end them directly like this.
To manage transactions, you'd either need a worker process as a loadable module, or to control the transaction from a client through a connection.

Store the query result in variable using postgresql returns wrong answer

I have faced a strange problem,I am using postgresql and I want to store result of a query into a variable. this is my code in function
select "GRIDS"."IDcluster" into clusterIDrow1 from "GRIDS" where "IDraster"=arow."IDraster";
RAISE DEBUG 'arow.IDraster is %',arow."IDraster";
RAISE DEBUG 'clusterIDrow1 is %',clusterIDrow1;
this gives me
DEBUG: arow.IDraster is 1
DEBUG: clusterIDrow1 is 0
but it must return clusterIDrow1 =44 since this query returns 44
select "GRIDS"."IDcluster" from "GRIDS" where "IDraster"=1;
can you please help me find out my mistake?
this is all of my function
CREATE OR REPLACE FUNCTION public.similarity_cal(c double precision,t double precision)
RETURNS text AS
$func$
DECLARE
num integer=1;
algabra raster;
width integer;
height integer;
sumOfsimilarity integer=1;
arow record;
secrow record;
clusterIDrow1 double precision;
clusterIDrow2 integer;
result text;
BEGIN
set client_min_messages to 'debug';
UPDATE public."GRIDS" SET "IDcluster"=0;
FOR arow IN
SELECT *
FROM "rasters"
LOOP
FOR secrow IN
SELECT *
FROM "rasters"
LOOP
SELECT ST_width(arow.rast),ST_height(arow.rast) into width,height As pvc ;
RAISE DEBUG 'first loop';
RAISE DEBUG ' width %', width;
RAISE DEBUG ' height %', height;
CONTINUE WHEN arow.rid=secrow.rid;
RAISE DEBUG 'start run algabra';
SELECT
ST_MapAlgebra(
arow.rast, 1,
secrow.rast, 1,
'(abs([rast2.val] - [rast1.val]))'
) into algabra AS algabraTable;
RAISE DEBUG 'end run algabra';
RAISE DEBUG 'start run count';
SELECT sum((pvc).count) into sumOfsimilarity
FROM (SELECT ST_ValueCount(algabra) As pvc) As foo where (pvc).value<c;
RAISE DEBUG 'end run count, value is %',sumOfsimilarity;
RAISE DEBUG 'Similarity condition is %',width*height*t;
IF (sumOfsimilarity>=width*height*t) THEN
RAISE DEBUG 'similarity is true';
select "GRIDS"."IDcluster" into clusterIDrow1 from "GRIDS" where "IDraster"=arow."IDraster";
RAISE DEBUG 'arow.IDraster is %',arow."IDraster";
RAISE DEBUG 'clusterIDrow1 is %',clusterIDrow1;
IF (clusterIDrow1<>0) THEN
select "IDcluster" into clusterIDrow2 from "GRIDS" where "IDraster"=secrow."IDraster";
RAISE DEBUG 'clusterIDrow2 is %',clusterIDrow2;
IF (clusterIDrow2=0) THEN
RAISE DEBUG 'update grids %',secrow."IDraster";
UPDATE public."GRIDS"
SET "IDcluster"=clusterIDrow1 WHERE "IDraster"=secrow."IDraster";
END IF;
ELSE
UPDATE public."GRIDS"
SET "IDcluster"=num WHERE "IDraster"=arow."IDraster";
num=num+1;
END IF;
END IF;
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql;