PostgreSQL IF-THEN-ELSE control structure - sql

Why do I always get the following error from Postgres?
syntax error at or near "IF"
I read PostgreSQL: Documentation: 8.3: Control Structures. First I tried to execute a difficult query (with subquery), but then I tried to execute a simple one like this:
IF 2 <> 0 THEN select * from users; END IF;
The error is still the same. What am I doing wrong?

IF 2 <> 0 THEN select * from users; END IF;
You cannot use PL/pgSQL statements outside plpgsql functions. And if this fragment is from plpgsql function, then it is nonsense too. You cannot directly return result of query like T-SQL does.
CREATE OR REPLACE FUNCTION test(p int)
RETURNS SETOF users AS $$
BEGIN
IF p = 1 THEN
RETURN QUERY SELECT * FROM users;
END IF;
RETURN;
END;
$$ LANGUAGE plpgsql;
When you would get some result from function, you have to use RETURN statement - plpgsql knows only function, it doesn't support procedures - so unbounded SELECT has not sense.

You're not enclosing that PL/pgSQL control structure in an anonymous block or a PL/pgSQL function.
For the SQL version of this control structure see the docs for CASE.

You're not enclosing that PL/pgSQL. They need to be enclosed with anonymous code block. Example for your code:
DO $$ BEGIN
IF 2 <> 0 THEN select * from users; END IF;
END$$;

Related

pass a query to row_to_json function inside a store procedure in postgres

I am trying to write a store procedure where I pass a query into a stored procedure which will internally call the row_to_json function. We have multiple DBMS (Oracle, SQL Server & postgres) and want to write a generic procedure which will act as a wrapper for the json handling function.
I am looking at something like this
CREATE OR REPLACE FUNCTION proj.sql_row_to_json(sql_t text)
RETURNS json
LANGUAGE sql
AS $function$
with t as (select sql_t )
select row_to_json(t) from t;
$function$
I would call function in my application as
select SQL_ROW_TO_JSON('<sql query>') -> which should work across different databases
But the above procedure gives me the below result instead of the actual data.
select SQL_ROW_TO_JSON('SELECT id,name FROM emp')
{"sql_t":"SELECT id,name FROM emp"}
You have to use plpgsql and dynamic sql.
create or replace function sql_row_to_json(sql_t text)
returns json language plpgsql as $function$
declare rslt json;
begin
execute
format($ex$
select json_agg(row_to_json(t))
from (%s) t
$ex$, sql_t)
into rslt;
return rslt;
end;
$function$;
select sql_row_to_json('select 1 as id, ''abcd'' as value');
sql_row_to_json
-------------------------
{"id":1,"value":"abcd"}
(1 row)
Note that the function is prone to SQL-injection attacks.

Return set of records with unknown table_name

I want to return a row from a table with this function (I don't know the name of the table, it's random)
CREATE FUNCTION foo( text ) RETURNS setof record AS $$
DECLARE
table_name ALIAS FOR $1;
BEGIN
SELECT * from table_name ;
END
$$ LANGUAGE plpgsql;
and then I want to do this:
select col1,col2 from foo('bar');
Any ideas?
SQL demands to know the return type at call time. And functions require you to define a return type as well. What you are after is not trivial.
If you don't know the return type at call time, you are basically out of luck. You cannot solve the problem with a single function call.
If you know the type at call time, there is an option with polymorphic types and dynamic SQL with EXECUTE:
CREATE OR REPLACE FUNCTION f_data_of_table(_tbl_type anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_tbl_type);
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM f_data_of_table(NULL::my_table_name);
Details in this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Be wary of SQL injection:
Table name as a PostgreSQL function parameter
Only makes sense if you do more than just SELECT * FROM tbl, or you'd simply use the SQL command.
Aside:
Do not use ALIAS to attach names to parameter values. That's outdated and discouraged. Use named parameters instead.

Write a PL/pgSQL function so that FOUND is not set when "nothing" is found?

I am just starting out on functions in PostgreSQL, and this is probably pretty basic, but how is this done?
I would like to be able to use the following in a function:
PERFORM id_exists();
IF FOUND THEN
-- Do something
END IF;
where the id_exists() function (to be used with SELECT and PERFORM) is:
CREATE OR REPLACE FUNCTION id_exists() RETURNS int AS $$
DECLARE
my_id int;
BEGIN
SELECT id INTO my_id
FROM tablename LIMIT 1;
RETURN my_id;
END;
$$ LANGUAGE plpgsql;
Currently, even when my_id does not exist in the table, FOUND is true, presumably because a row is still being returned (a null integer)? How can this be re-written so that an integer is returned if found, otherwise nothing at all is?
Your assumption is correct, FOUND is set to TRUE if the last statement returned a row, regardless of the value (may be NULL in your case). Details in the manual here.
Rewrite to, for instance:
IF id_exists() IS NOT NULL THEN
-- Do something
END IF;
Or rewrite the return value of your function with SETOF so it can return multiple rows - or no row! Use RETURN QUERY like I demonstrate. You can use this function in your original setting.
CREATE OR REPLACE FUNCTION id_exists()
RETURNS SETOF int LANGUAGE plpgsql AS
$BODY$
BEGIN
RETURN QUERY
SELECT id
FROM tablename
LIMIT 1;
END;
$BODY$;
Or, even simpler with a language SQL function:
CREATE OR REPLACE FUNCTION id_exists()
RETURNS SETOF int LANGUAGE sql AS
$BODY$
SELECT id
FROM tablename
LIMIT 1;
$BODY$;

Are there any way to execute a query inside the string value (like eval) in PostgreSQL?

I want to do like this:
SELECT (EVAL 'SELECT 1') + 1;
Are there any way to do like this (EVAL) in PostgreSQL?
If the statements you are trying to "eval" always return the same data type, you could write an eval() function that uses the EXECUTE mentioned by Grzegorz.
create or replace function eval(expression text) returns integer
as
$body$
declare
result integer;
begin
execute expression into result;
return result;
end;
$body$
language plpgsql
Then you could do something like
SELECT eval('select 41') + 1;
But this approach won't work if your dynamic statements return something different for each expression that you want to evaluate.
Also bear in mind that this opens a huge security risk by running arbitrary statements. If that is a problem depends on your environment. If that is only used in interactive SQL sessions then it isn't a problem.
NOTES
The language PLpgSQL syntax have many ways to say:
Y := f(X);
The EXECUTE clause is only for "dynamic execution" (less performance),
EXECUTE 'f(X)' INTO Y;
Use Y := f(X); or SELECT for execute static declarations,
SELECT f(X) INTO Y;
Use PERFORM statment when discard the results or to work with void returns:
PERFORM f(X);
I'd go with data type text since it's more flexible using casting operators like ::int if needed:
create or replace function eval( sql text ) returns text as $$
declare
as_txt text;
begin
if sql is null then return null ; end if ;
execute sql into as_txt ;
return as_txt ;
end;
$$ language plpgsql
-- select eval('select 1')::int*2 -- => 2
-- select eval($$ select 'a'||1||'b' $$) -- => a1b
-- select eval( null ) -- => null
I also added this and another eval( sql, keys_arr, vals_arr ) function supporting some custom key-value substitutions, e.g. for handy :param1 substitutions to postgres-utils
I am not sure if it suits you but PostgreSQL has EXECUTE statement.
Good idea. You can modify to perform direct expressions:
create or replace function eval(expression text) returns integer
as
$body$
declare
result integer;
begin
execute 'SELECT ' || expression into result;
return result;
end;
$body$
language plpgsql;
To run just type this:
SELECT eval('2*2');
Assuming that most sql queries are a part of a bigger system, there mostly will be cases where you form a query with your backend code and then execute it.
So if that’s the case for you, you can just use subselects or common table expressions that are put into your query string by the backend code before execution.
I have trouble coming up with cases where the solution you want works and my solution doesn’t, apart from not having any backend app, of course.

How do I pass in the array output of SQL query into a PostgreSQL (PL/pgSQL) function?

I am able to do the following in SQL where an "array" of user_ids are passed into the where clause of a SQL query.
select * from users where id in (select user_id from profiles);
I would like to do the same thing but pass the "array" into a PostgreSQL (PL/pgSQL) function as shown below. How do I declare the function and work with the "array" within the function?
select * from users_function(select user_id from profiles);
CREATE OR REPLACE FUNCTION users_function(....)
RETURNS void AS
$BODY$
....
Declare an array datatype [] in the function then use the aggregate function array_agg to transform the select statement into an array.
CREATE OR REPLACE FUNCTION users_function(myints integer[])
$$
BEGIN
-- you need to find the bounds with array_lower and array_upper
FOR i in array_lower(myints, 1) .. array_upper(myints, 1) LOOP
Raise Notice '%', myints[i]::integer;
END LOOP;
END;
$$
select * from users_function(array_agg((select user_id from profiles)));
I could not get the nate c's array_agg approach as I described above. This is an option:
select * from test_array('{1,2}');
CREATE OR REPLACE FUNCTION test_array(user_ids integer[])
RETURNS void AS
$$
declare
begin
FOR i in array_lower(user_ids, 1) .. array_upper(user_ids, 1) LOOP
RAISE NOTICE '%', user_ids[i]::integer;
END LOOP;
end
$$
LANGUAGE plpgsql;