Writing Errors to File in Postgresql - sql

I want to write errors to a file in Postgresql, so whenever error occurs in a function it writes to a file, so I have captured the error and I pass on the error to another function and the return of that function is written to a file. Below is a sample function,
CREATE OR REPLACE FUNCTION test(TEXT)
RETURNS VOID
STRICT
LANGUAGE plpgsql
AS $$
declare STATEMENT TEXT;
declare sql_code TEXT;
BEGIN
BEGIN
EXECUTE 'DROP TABLE ' || $1;
EXCEPTION WHEN others THEN
sql_code := SQLERRM;
STATEMENT := 'COPY (select * from test1('''||sql_code||''')) to ''/tmp/errors.txt''';
EXECUTE STATEMENT;
RETURN;
END;
RAISE NOTICE 'Dropped table successfully %', $1;
RETURN;
END;
$$;
CREATE OR REPLACE FUNCTION test1(TEXT)
RETURNS TEXT
STRICT
LANGUAGE plpgsql
AS $$
declare error TEXT;
BEGIN
error := $1;
RETURN STATEMENT;
END;
$$;
This thing works fine but I want to know if there is any good way to capture the error and log it to a file ? Please suggest.

Changing logging levels would be preferable though there may be good reasons to prevent this.
Failing that you could write a secondary logging routine in, say, pl/perlu, or pl/python, which could write to a file. Just be careful that you don't overwrite your data files O.o.....

Related

plpgsql in EDB: SPL procedure with SPL-style OUT parameter or a function cannot be invoked using CALL in PL/pgSQL

I'm trying to run natively correct plpgsql code in EDB environment. Unfortunately there is a problem. Interestingly, the first run does not generate an error. Restarts generate an error, but after a few repetitions, there is no error again.
In PosgreSQL, the syntax DOES NOT CAUSE any problems.
Below is an example:
CREATE OR REPLACE PROCEDURE test1()
AS $procedure$
DECLARE
BEGIN
null;
END;
$procedure$
LANGUAGE plpgsql
;
CREATE OR REPLACE PROCEDURE test2()
AS $procedure$
DECLARE
BEGIN
call test1();
END;
$procedure$
LANGUAGE plpgsql
;
CREATE OR REPLACE PROCEDURE test3()
AS $procedure$
DECLARE
BEGIN
call test2();
END;
$procedure$
LANGUAGE plpgsql
;
And now try to run it in postgresql way and EDB way:
--run it few times as edb - error occurs randomly
begin
test3();
end;
--once again as plpgs - no error occurs but... check last call
do
$$
begin
CALL test3();
end;
$$
--once again as plpgs with exception block. - now error occurs same as edb call
do
$$
declare
v_sqlstate text;
v_message text;
v_context text;
begin
CALL test3();
EXCEPTION
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS v_sqlstate = returned_sqlstate,v_message = message_text,v_context = pg_exception_context;
RAISE NOTICE 'sqlstate: %,message: %,context: %', v_sqlstate,v_message,v_context;
end;
$$
Error is:
ERROR: SPL procedure with SPL-style OUT parameter or a function cannot be invoked using CALL in PL/pgSQL
HINT: You might want to use SELECT instead.
CONTEXT: PL/pgSQL function test3() line 4 at CALL
edb-spl function inline_code_block line 2 at procedure/function invocation statement
SQL state: 42809
What am I missing???
I find that this might be a bug fixed in EPAS 13.8 (DB-1818).
https://www.enterprisedb.com/docs/epas/13/epas_rel_notes/epas13_8_12_rel_notes/
I guess you may try your test in this or later new minor version.
Best Regards.

Postgres set working schema from inside of function

I am facing a problem I have a function which used to create schema and tables inside that schema after table creation I am calling a function which supposed to populate this schema however feels like the second function doesn't set the working schema and throws the error that object doesn't exits (
ERROR: relation "table" does not exist
LINE 1: INSERT INTO table
here is what the function looks like.
CREATE OR REPLACE FUNCTION create_schema(
t_shema character varying,
t_country TEXT
)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
DECLARE
tname text := t_shema;
tschem_name text := tname||'_work';
tsql_dyn text ;
tschema_check numeric := 0 ;
BEGIN
SELECT 1
INTO TSCHEMA_CHECK
FROM PG_NAMESPACE
WHERE NSPNAME = TSCHEM_NAME;
IF TSCHEMA_CHECK = 1 THEN
RETURN 'Schema '||tschem_name ||' Already exists';
ELSE
tsql_dyn := 'CREATE SCHEMA '||tschem_name||';';
raise notice 'EXECUTE %', tsql_dyn;
EXECUTE tsql_dyn;
tsql_dyn := 'SET search_path TO '||tschem_name ||';';
raise notice 'EXECUTE %', tsql_dyn;
EXECUTE tsql_dyn;
--other DDLs
---execute of function which populates freshly created schema
SELECT public.populate_empty_schema(tname, t_country);
RETURN tname ||' created';
END IF;
END;
$BODY$;
The second function also has a statement as first which sets the working schema.
Both functions work fine if get called separately, trows error only if second get called from the first
Your function is vulnerable to SQL injection.
Instead of
tsql_dyn := 'CREATE SCHEMA '||tschem_name||';';
write
tsql_dyn := format('CREATE SCHEMA %I', tschem_name);
To set the search_path in populate_empty_schema, pass the schema name to the function and have it execute
PERFORM set_config('search_path', v_schema, TRUE);
feels like Postgres cannot set working schema from the second function, my solution. was just deleting the set search path from the second function and it is working. feels like it couldn't set it as shcema is not committed at the point when the 2-nd function gets called.

Evaluate dynamic condition in PL/pgSQL using EXECUTE

I have a function that needs to dynamically validate the input based on the value type. It does this by finding the constraint in another table, but for simplicity I provide the constraint as well in the function below.
This other table contains (value_type, value_constraint), where value_constraint is a text field that contains e.g. value::int > 0. I need to dynamically check this constraint within my insert function. I was trying to do that using EXECUTE as seen below, but it's not working.
How do I dynamically execute a condition statement and get the value as a boolean into v_successful_insert?
CREATE OR REPLACE FUNCTION insert_value(p_value_type text, p_value_constraint text, p_value text) RETURNS boolean
AS $$
DECLARE
v_successful_insert bool;
BEGIN
EXECUTE p_value_constraint INTO v_successful_insert;
IF v_successful_insert THEN
INSERT INTO my_table (value_type, value)
VALUES (p_value_type, p_value);
END IF;
RETURN v_successful_insert;
END;
$$
LANGUAGE plpgsql volatile;
The code is run on Postgresql 10.6.
You need to do a SELECT in the execute. For example:
DO $$
DECLARE
p_value TEXT := 'x';
p_value_constraint TEXT := '::int > 0';
result BOOLEAN;
BEGIN
BEGIN
EXECUTE 'SELECT $1' || p_value_constraint
INTO result
USING p_value;
EXCEPTION
WHEN INVALID_TEXT_REPRESENTATION THEN
result := FALSE;
END;
RAISE NOTICE '%', result;
END $$
Prints FALSE

wrong number or types of arguments in call (while calling a procedure/function)

If I want to catch this error using exception handling, what are the things that I need to take care of?
wrong number or types of arguments in call (while calling a
procedure/function)
I trying was in different way. Could you please explain. I have a function:
create or replace function test5(v varchar2) return varchar as
begin
execute immediate 'begin sweet.g:=:v;end;'
using in v;
return sweet.g;
exception
when others then
return sqlcode||' '||sqlerrm;
end test5;
And a package spec and body:
create or replace package SWEET as
function c ( v varchar2,V2 VARCHAR2) return varchar2;
g varchar(100);
end;
/
create or replace package body SWEET as
function c(v varchar2, V2 varchar2) return varchar2 as
begin
return v||'hi'|| V2;
end c;
end;
/
when I execute the statement below, I was not able to catch 'wrong number or type of arguments'
select test5(sweet.c(,'hello')) from dual;
You should be able to get most of the answers in the PL/SQL manual, but when you are trying to trap an error that isn't one of the predefined ones you have to do something like:
DECLARE
deadlock_detected EXCEPTION;
PRAGMA EXCEPTION_INIT(deadlock_detected, -60);
BEGIN
... -- Some operation that causes an ORA-00060 error
EXCEPTION
WHEN deadlock_detected THEN
-- handle the error
END;
replacing -60 with your actual error, and deadlock_detected with whatever you wish to call it.
Let's say you have a procedure that takes in two numbers as arguments and outputs them:
create procedure testProc (p_param1 in number, p_param2 in number) is
begin
dbms_output.put_line('params: ' || p_param1 || ' ' || p_param2);
end;
If you execute this:
begin
testProc(13,188);
end;
You get output of: params: 13 188
If you do this:
begin
testProc(13);
exception when others then
dbms_output.put_line('SQLERRM: ' || SQLERRM);
end;
You get an error: PLS-00306: wrong number or types of arguments in call to 'TESTPROC'
To prevent this and catch the error, you can use dynamic SQL:
declare
v_sql varchar2(50);
v_result number;
begin
v_sql := 'begin testProc(13); end;';
execute immediate v_sql into v_result;
exception
when others then
dbms_output.put_line('SQLERRM: ' || SQLERRM);
end;
That will execute, and the error message will be displayed to dbms_output. In the when others then block you can write any logic you want for what should happen at that point.

NZSQL/CODE - How to use PRINT in Netezza

We are using Aginity Workbench for Netezza SQL and I was wondering if anyone knows of a equivalent in NZSQL for the TSQL "PRINT" function?
We use it for printing errors when someone tries to execute a query that would rewrite data when it shouldnt, and the only solution I am finding is using netezza command line "-t".
Thanks in advance!
You can use RAISE to do this, as documented here.
Here is an example:
CREATE OR REPLACE PROCEDURE RAISE_DEMO()
RETURNS VARCHAR(ANY)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
MYNAME varchar;
BEGIN
MYNAME := 'SCOTT';
RAISE NOTICE 'Hello, %', MYNAME;
END;
END_PROC;
TESTDB.ADMIN(ADMIN)=> call raise_demo();
NOTICE: Hello, SCOTT
RAISE_DEMO
------------
(1 row)
If you change NOTICE to EXCEPTION, then the execution will stop.
CREATE OR REPLACE PROCEDURE RAISE_DEMO()
RETURNS VARCHAR(ANY)
EXECUTE AS OWNER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
MYNAME varchar;
BEGIN
MYNAME := 'SCOTT';
RAISE EXCEPTION 'Hello, %, this is an exception', MYNAME;
RAISE NOTICE 'You should not see this message';
END;
END_PROC;
TESTDB.ADMIN(ADMIN)=> call raise_demo();
ERROR: Hello, SCOTT, this is an exception
TESTDB.ADMIN(ADMIN)=>
This behavior is in documented in the link I provided at the top.