Relation does not exist PLPGSQL - sql

I am trying to create a view inside the function using plpgsql which returns the x column of the "small" table which is defined as (x integer,y integer).
create or replace function skyline_naive2(dataset text) returns setof integer as
$$
declare
fullx text;
begin
fullx = dataset||'_skyline_naive2';
execute format('create view %s as select x,y from %s',fullx,dataset);
return query select x from fullx;
end
$$ language plpgsql;
select * from skyline_naive2('small');
It returns "relation fullx does not exist"
I understand that it is because there is no fullx relation, but I want to call the view using the variable name.
Any help will be

Use dynamic SQL for select (as you have used for create):
create or replace function skyline_naive2(dataset text) returns setof integer as
$$
declare
fullx text;
begin
fullx = dataset||'_skyline_naive2';
execute format('create view %I as select x,y from %I',fullx,dataset);
return query execute format('select x from %I', fullx);
end
$$ language plpgsql;

You need to EXECUTE your dynamic query:
RETURN QUERY EXECUTE 'SELECT x FROM ' || fullx;

Related

Return a table that already exist in postgresql function

I have a bunch of functions that return the same table schema, so I have to repeat the same table schema over and over between those functions' declarations, to make this example simple let's say we have two functions that return the same table schema:
Table: people
CREATE TABLE people(full_name TEXT, age integer);
Functions:
CREATE OR REPLACE FUNCTION get_people_by_age(_age integer)
RETURNS TABLE(full_name TEXT, age integer)
LANGUAGE PLPGSQL
AS
$$
BEGIN
RETURN QUERY SELECT * FROM people WHERE people.age = $1;
END
$$
CREATE OR REPLACE FUNCTION get_people_by_name(_full_name text)
RETURNS TABLE(full_name TEXT, age integer)
LANGUAGE PLPGSQL
AS
$$
BEGIN
RETURN QUERY SELECT * FROM people WHERE people.full_name = $1;
END
$$
Is there a way to refer to the existing table within the function declarations? I imagine something like this:
CREATE OR REPLACE FUNCTION get_people_by_age(_age integer)
RETURNS TABLE(people)
LANGUAGE PLPGSQL
AS
$$
BEGIN
RETURN QUERY SELECT * FROM people WHERE people.age = $1;
END
$$
CREATE OR REPLACE FUNCTION get_people_by_name(_full_name text)
RETURNS TABLE(people)
LANGUAGE PLPGSQL
AS
$$
BEGIN
RETURN QUERY SELECT * FROM people WHERE people.full_name = $1;
END
$$
Where instead of declaring the same schema in every function I refer to a table that already exists, is it possible?
Use returns setof
CREATE OR REPLACE FUNCTION get_people_by_name(_full_name text)
RETURNS setof people
LANGUAGE PLPGSQL
AS
$$
BEGIN
RETURN QUERY SELECT * FROM people WHERE people.full_name = _full_name;
END
$$;
Or a bit simpler as a SQL function:
CREATE OR REPLACE FUNCTION get_people_by_name(_full_name text)
RETURNS setof people
LANGUAGE sql
AS
$$
SELECT * FROM people WHERE people.full_name = _full_name;
$$;

How do I return a table from a function with a bespoke column name?

This function works:
CREATE OR REPLACE FUNCTION public.a()
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a from ztable';
END;
$function$;
But when I try to add some text to the column name:
CREATE OR REPLACE FUNCTION public.a(prefix text)
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a as $1_a from ztable' using prefix;
END;
$function$;
This just fails as a syntax error on $1.
Or:
CREATE OR REPLACE FUNCTION public.a(prefix text)
RETURNS TABLE(a text)
LANGUAGE plpgsql
AS $function$
BEGIN
return query execute
'select a as '||prefix||'_a from ztable';
END;
$function$;
select * from a('some prefix') doesn't work.
Is there some other syntax that does the job?
That's simply not possible. SQL does not allow dynamic column names.
You must assign a column alias with the call. Like:
SELECT a AS prefix_a FROM public.a();
Or in a column definition list directly attached to the function:
SELECT * FROM public.a() AS f(prefix_a);
Or, while dealing with a single output column, even just:
SELECT * FROM public.a() AS prefix_a;
See:
RETURNING rows using unnest()?

Call an Array result from select function to do update function with postgresql

I'm new with SQL functions and postgreSQL. I just try to select some mails of my compte table to update them afterwards so I create select and update functions but I have always the error:
"ERROR: query "SELECT emailAnonymisation()" returned more than one row
CONTEXT: PL/pgSQL function updateMail() line 5 during statement block local variable initialization"
The problem is in my update function but I don't know if it's only a variable problem or a function logical problem...
My select function
CREATE OR REPLACE FUNCTION emailAnonymisation()
RETURNS table (mail varchar)
LANGUAGE plpgsql
AS $$
BEGIN
return query
SELECT compte.mail
FROM compte
limit 100;
END
$$;
My update function where I call the emailAnonymisation() function and where the problem is I think
CREATE OR REPLACE FUNCTION updateMail()
RETURNS varchar[] AS
$BODY$
DECLARE
_tbl varchar[]:=emailAnonymisation();
t text;
BEGIN
FOREACH t IN ARRAY _tbl
LOOP
EXECUTE '
UPDATE ' || t || '
SET t = REPLACE(SUBSTR(t,LOCATE('#',t) + 1),"X")
WHERE LOCATE('#',t) > 0;';
END LOOP;
END;
$BODY$ LANGUAGE plpgsql;
the update call
select updateMail();
Try using SETOF:
CREATE OR REPLACE FUNCTION emailAnonymisation()
RETURNS SETOF string(mail varchar)
LANGUAGE plpgsql
AS $$
BEGIN
return query
SELECT compte.mail
FROM compte
limit 100;
END
$$;
Ok I have finally found what was the problem with the select I should have a return like this with RETURNS character varying[]
CREATE OR REPLACE FUNCTION emailAnonymisation()
RETURNS character varying[]
AS $$
DECLARE
result character varying[];
BEGIN
SELECT ARRAY( SELECT compte.mail as mail
FROM compte
WHERE mail IS NOT NULL
limit 100)
into result;
return result;
END;
$$
LANGUAGE plpgsql;
And for the update the same type as the select function
CREATE OR REPLACE FUNCTION updateMail()
RETURNS character varying(150) AS
$BODY$
DECLARE
_tbl character varying[]:=emailAnonymisation();
mail text;
endUrl text := '#mail.com';
BeginningUrl text := random_string(15);
BEGIN
FOREACH mail IN ARRAY _tbl
LOOP
UPDATE ' || t || '
SET t = REPLACE(SUBSTR(t,LOCATE('#',t) + 1),"X")
WHERE LOCATE('#',t) > 0;';
END LOOP;

Sql create a view X_myview where X is given into a function(X text) which creates the view

Trying to create a function that given the name of a table will create a function with the table name concatenated to _myView
eg if the name of the table is students the function will create a view called students_myView
Here's what I have so far.
create or replace function skyline_naive(tableName text)
as $$
declare
name text;
begin
name:= $1;
create or replace view name_myView as select * from name;
end
$$ language plpgsql;
A dynamic command in PLPgSQL must be executed as a string with EXECUTE statement.
To make it work, you should write it like this:
create or replace function skyline_naive(tableName text)
returns void as $$
begin
execute format('create or replace view %1$s_myView as select * from %1$s;', tableName)
return;
end;
$$ language
plpgsql;

Create function which return size of table

My code:
CREATE OR REPLACE FUNCTION sizeOfTableFunction
(
p_tableName varchar(100)
)
RETURNS integer
AS $$
DECLARE
p_tableSize integer;
BEGIN
SELECT count(*) into p_tableSize from p_tableName;
return p_tableSize;
END;
$$ LANGUAGE plpgsql STRICT;
Function has been created properly:
CREATE FUNCTION
Execute:
SELECT * FROM sizeOfTableFunction('Run');
Output - problem with executing the function?:
mydb=> SELECT * FROM sizeOfTableFunction('Run');
ERROR: relation "p_tablename" does not exist
LINE 1: SELECT count(*) from p_tableName
^
QUERY: SELECT count(*) from p_tableName
CONTEXT: PL/pgSQL function "sizeoftablefunction" line 5 at SQL statement
You need dynamic SQL for that:
CREATE OR REPLACE FUNCTION sizeOfTableFunction
(
p_tableName varchar(100)
)
RETURNS integer
AS $$
DECLARE
p_tableSize integer;
BEGIN
execute 'SELECT count(*) from '||p_tableName into p_tablesize; -- this is the difference
return p_tableSize;
END;
$$ LANGUAGE plpgsql STRICT;
To be safe, it's better to use the quote_ident function, just in case your tablename contains special characters. It also gives you some protection from SQL injection.
execute 'SELECT count(*) from '||quote_ident(p_tableName) into p_tablesize;