Executing Postgresql function from Python - postgresql

I have created a function (taken from wiki.postgresql.org) in my db for getting approximate row count.
CREATE FUNCTION ais_history.count_estimate(query text)
RETURNS integer
LANGUAGE plpgsql AS
$func$
DECLARE
rec record;
rows integer;
BEGIN
FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
rows := substring(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
EXIT WHEN rows IS NOT NULL;
END LOOP;
RETURN rows;
END
$func$;
When I use the function from the psql cli, it works fine.
db=> SELECT count_estimate('SELECT * FROM schema.table_name');
count_estimate
----------------
4
(1 row)
However, when I run it from Python, using either SQLalchemy core or psycopg2, it gives an error.
conn.autocommit = True
cursor = conn.cursor()
cursor.execute("SELECT count_estimate('SELECT * FROM schema.table_name');")
conn.commit()
conn.close()
Error output
UndefinedFunction Traceback (most recent call last)
Input In [91], in <cell line: 8>()
5 conn.autocommit = True
6 cursor = conn.cursor()
----> 8 cursor.execute("SELECT count_estimate('SELECT * FROM schema.table_name');")
10 conn.commit()
11 conn.close()
UndefinedFunction: function count_estimate(unknown) does not exist
LINE 1: SELECT count_estimate('SELECT * FROM schema.table_name...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
How can I run the raw sql to get the function output?

Related

How to implement execute command inside for loop - postgres, dbeaver

I am new to postgres. I need to create a function that will take a list of all the tables in the database whose names are stored in one table and then delete the records of all the tables that are older than x days and have a certain row_status. Some tables do not have a row_status column.
I get an error when I try to save a written function in dbeaver -> ERROR: syntax error at or near "||"
create function delete_old_records1(day1 int, row_status1 character default null, row_status2 character default null)
returns void
language plpgsql
as $$
declare
c all_tables1%rowtype;
begin
for c in select * from all_tables1 loop
if exists(SELECT column_name FROM information_schema.columns
WHERE table_schema = 'yard_kondor' AND table_name =c.table_name AND column_name = 'row_status') then
execute 'delete from '||c.table_name||' where row_create_datetime>current_date-day1+1 and
row_status in (coalesce(row_status1,''P''), row_status2)';
else
execute 'delete from '||c.table_name||' where row_create_datetime>current_date-day1+1';
raise notice 'Table '||c.table_name||' does not have row_status column';
end if;
end loop;
return;
commit;
end;
$$
Your immediate problem is this line:
raise notice 'Table '||c.table_name||' does not have row_status column';
That should be:
raise notice 'Table % does not have row_status column', c.table_name;
However, your function could be improved a bit. In general it is highly recommended to use format() to generate dynamic SQL to properly deal with identifiers. You also can't commit in a function. If you really need that, use a procedure.
create function delete_old_records1(day1 int, row_status1 character default null, row_status2 character default null)
returns void
language plpgsql
as $$
declare
c all_tables1%rowtype;
begin
for c in select * from all_tables1
loop
if exists (SELECT column_name FROM information_schema.columns
WHERE table_schema = 'yard_kondor'
AND table_name = c.table_name
AND column_name = 'row_status') then
execute format('delete from %I
where row_create_datetime > current_date - %J + 1
and row_status in (coalesce(row_status1,%L), row_status2)', c.table_name, day1, 'P');
else
execute format('delete from %I where row_create_datetime > current_date - day1 + 1', c.table_name);
raise notice 'Table % does not have row_status column', c.table_name;
end if;
end loop;
return;
-- you can't commit in a function
end;
$$
Thank you for answer. Now I'm able to save function, but currently I have a problem with running the function.
I started the function with:
DO $$ BEGIN
PERFORM "delete_old_records1"(31,'P','N');
END $$;
I started script with ALT+X (also tried select delete_old_records1(31,'P','N');) and have this error:
SQL Error [42703]: ERROR: column "day1" does not exist
Where: PL/pgSQL function delete_old_records1(integer,character,character) line 11 at EXECUTE statement
SQL statement "SELECT "delete_old_records1"(31,'P','N')"
PL/pgSQL function inline_code_block line 2 at PERFORM

Dynamic query with single quote

I'm trying to create a stored procedure that will create a select statement.
My procedure looks like below.
CREATE OR REPLACE PROCEDURE record_example()
LANGUAGE plpgsql
AS $$
DECLARE
unload_query text;
BEGIN
unload_query := 'query = ('''select * from my_table''')';
insert into query values (unload_query);
END;
$$;
But its throwing error. Im not able to close the single quote properly.
LINE 1: SELECT 'query = ('''select * from my_table''')'
^
QUERY: SELECT 'query = ('''select * from my_table''')'
CONTEXT: SQL statement in PL/PgSQL function "record_example" near line 5
Expected output:
Unload query = query = (select * from my table)
If you want the result of a query inside a string, you need to use the string concatenation operator. Also, subqueries must be surrounded by parentheses.
unload_query := 'query = (''' || (SELECT * FROM my_table) || ''')';
This will fail if the query returns more than a single row.
If you want the literal string in query, you have too many quotes:
unload_query := 'query = (''select * from my_table'')';

How to loop inside cursor in PostgreSQL

I have a function
drop function ProcessReward();
CREATE OR REPLACE FUNCTION ProcessReward()
RETURNS text AS $$
DECLARE
sessionid NO SCROLL CURSOR FOR SELECT pg."Setting",pg."UserId",pg."Id" FROM "Development"."PersonGame" pg inner join "Development"."Game" g on g."Id" = pg."GameId" pg."GameId"=1 for read only;
titles TEXT DEFAULT '';
rec record;
jsonrec record;
jsonrecord record;
BEGIN
OPEN sessionid;
loop
FETCH sessionid INTO rec;
if not found then
exit ;
end if;
EXECUTE 'select * from "Development"."GameRecipient" where "PersonGameId"=$1' into jsonrecord using rec."Id";
--I want to loop here every row returned by above query
--loop start
-- do your task
--loop end
end loop;
return titles;
END;
$$ LANGUAGE plpgsql;
My query
EXECUTE 'select * from "Development"."GameRecipient" where
"PersonGameId"=$1' into jsonrecord using rec."Id";
returns
col1 col2 col3
123 324 444
345 222 765
I want to process all rows returned by above query,How to achieve this in PostgreSQL.

pgsql sql functions sequential execution

If I have these two Postgres function definitions saved in two seperate .sql files:
CREATE OR REPLACE FUNCTION column_exists(tablename text, colname text) RETURNS boolean AS
$BODY$
DECLARE
q text;
field_name text;
onerow record;
BEGIN
q = 'SELECT column_name FROM information_schema.columns WHERE table_name='''||tablename||''' AND table_schema =''public''';
FOR onerow IN EXECUTE q
LOOP
field_name := onerow.column_name;
IF ((field_name = colname)) then
RETURN true;
END IF;
END LOOP;
RETURN false;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION correct_col_names() RETURNS VOID AS
$BODY$
DECLARE
q boolean;
BEGIN
-- rename name column to Name
select column_exists('National_Parks', 'name') as q;
IF q = TRUE THEN
alter table "National_Parks"
rename column name to "Name";
END IF;
-- remance descriptio column to description
select column_exists('National_Parks', 'descriptio') as q;
IF q = TRUE THEN
alter table "Natioanl_Parks"
rename column descriptio to "Description";
END IF;
END
$BODY$
LANGUAGE plpgsql
What is the syntax I need to use to call the sequentially, say in another script? I tried
select correct_col_names()
and this returns the following error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "correct_col_names" line 7 at SQL statement
********** Error **********
ERROR: query has no destination for result data
SQL state: 42601
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Context: PL/pgSQL function "correct_col_names" line 7 at SQL statement
TIA.
The problem is that you have SELECT statements that aren't doing anything with the data. Your
select column_exists('National_Parks', 'name') as q;
should be
select column_exists('National_Parks', 'name') INTO q;
The as simply aliases the result as "q" for that query, it doesn't actually put it into the q variable.
Your syntax for calling the functions (select correct_col_names()) is correct for SQL. Once you fix the two errors in that function, it should work.
However, if you were to try select correct_col_names() inside another PL/PGSQL function, you would get the same error, because the select statement isn't actually doing anything with the results. perform correct_col_names() would run without error, because PERFORM is PL/PGSQL syntax for calling something when you don't want to save the result.

PL/PGSQL function, having trouble accessing a returned result set from psycopg2

I have this pl/pgsql function:
CREATE OR REPLACE FUNCTION get_result(id integer) RETURNS SETOF my_table AS $
DECLARE
result_set my_table%ROWTYPE;
BEGIN
IF id=0 THEN
SELECT INTO result_set my_table_id, my_table_value FROM my_table;
ELSE
SELECT INTO result_set my_table_id, my_table_value FROM my_table WHERE my_table_id=id;
END IF;
RETURN;
END;
$ LANGUAGE plpgsql;
I am trying to use this with Python's psycopg2 library. Here is the python code:
import psycopg2 as pg
conn = pg.connect(host='myhost', database='mydatabase', user='user', password='passwd')
cur = conn.cursor()
return cur.execute("SELECT * FROM get_result(0);") # returns NoneType
However, if i just do the regular query, I get the correct set of rows back:
...
return cur.execute("SELECT my_table_id, my_table_value FROM mytable;") # returns iterable result set
Theres obviously something wrong with my pl/pgsql function, but I can't seem to get it right.
I also tried using
RETURN result_set;
instead of just
RETURN
in the 10th line of my plpgsql function, but got an error from postgres.
Please read the error you're getting when you change "RETURN;" to "RETURN result_set;". And then read the following - "38.6.1.2. RETURN NEXT and RETURN QUERY".
A hint - in your case you need "RETURN QUERY".