Reference psql parameter inside PL/pgSQL anonymous block - postgresql

I'd like to pass a parameter to an anonymous PL/pgSQL block via psql command line, then check that parameter in a conditional.
The relevant part of the SQL is here:
do $$
begin
if (':para' = 1) then
-- statements;
end if;
end $$
;
I call this script as such:
psql -d dbname -v para=1 < script.sql
I receive the error:
ERROR: invalid input syntax for integer: ":para"
LINE 1: SELECT (':para' = 1)
^
QUERY: SELECT (':para' = 1)
CONTEXT: PL/pgSQL function inline_code_block line 3 at IF
I tried using the case/when paradigm, which did not work either.
I am guessing that a psql parameter is not compatible with PL/pgSQL? Ultimately, my goal is to run a single delete statement if I pass 1 as a psql parameter, and not run it if I pass 0.

The psql parser can't see what is inside strings. This might be what you want:
delete from t
where :para = 1
Do it outside of an anonymous block. If you really need PL/pgSQL use a parameterized function:
create or replace function f(_para integer)
returns void as $$
begin
if _para = 1 then
--statements
end if;
end; $$ language plpgsql;
And your script file will have:
select f(:para);
If you do not want to permanently add a function to the db do it all inside the script:
drop function if exists f_iu7YttW(integer);
create or replace function f_iu7YttW(_para integer)
returns void as $$
begin
if _para = 1 then
--statements
end if;
end; $$ language plpgsql;
select f_iu7YttW(:para);
drop function f_iu7YttW(integer);
Give the function an unique name so you do not run the risk of dropping something else.

Related

How to declare a variable in postgres script?

I am fairly new to Postgres and I cannot believe how difficult I am finding just to declare a variable. I did come across other SO posts, but none of them helped in my situation. All I want is to write the a script like below in postgres:
declare #age int = 10;
select * from person p where p.age > #age;
Based on the SO post here, I tried:
DO
$$
DECLARE
overTheAgeOf int := 15;
BEGIN
select *
from person
where age > overTheAgeOf;
END
$$;
This gives me error: [42601] ERROR: query has no destination for result data
Then I tried returning the result of the script:
return (select *
from person
where age > overTheAgeOf);
That gave me another error: ERROR: RETURN cannot have a parameter in function returning void
How do declare a variable and use it in script(s) that follows?
You are confused on several levels.
There is the query language SQL, and there is the procedural language PL/pgSQL. The only connection is that
you can run SQL statements from PL/pgSQL code
you can have PL/pgSQL code in the body of the SQL statements DO and CREATE FUNCTION/PROCEDURE.
There are variables in PL/pgSQL, which are defined in the DECLARE section, but there are no variables in SQL.
DO statements cannot return any values.
If you want to use PL/pgSQL variables, and you want to return values, you'll have to use a function. An example:
CREATE FUNCTION getpersons() RETURNS SETOF person
LANGUAGE plpgsql AS
$$DECLARE
overTheAgeOf int := 15;
BEGIN
RETURN QUERY
SELECT *
FROM person
WHERE age > overTheAgeOf;
END;$$;
SELECT getpersons();
There is the alternative of using variables on the client. With the psql client, you could use:
\set overTheAgeOf 15
SELECT *
FROM person
WHERE age > :overTheAgeOf;
The good structure is like this :
DO
LANGUAGE plpgsql $$
DECLARE
variable int := 0;
BEGIN
-- your code
raise notice '%', variable::varchar;
END;
$$;

Postgres - ERROR: syntax error at or near "IF"

I have a function and I need to do different select based on inputid. so I have added the condition as below.
....
begin
IF(iv_binrocess = 1 AND IsNull(iv_binno,'') != '') THEN
...//some process here
END If;
...
End
When I run the function, I got the error "
ERROR: syntax error at or near "IF"
I referred many sites and tutorials, nothing helped me.
Can anyone help me to fix this issue?
Thanks
WAG, since you deleted most of the important information: You're trying to create a function with a PL/pgsql body, but declaring it SQL.
craig=> CREATE FUNCTION test() RETURNS void LANGUAGE sql AS
$$ BEGIN IF TRUE THEN PERFORM 1; END IF; END; $$ ;
ERROR: syntax error at or near "IF"
LINE 1: ...TION test() RETURNS void LANGUAGE sql AS $$ BEGIN IF TRUE TH...
Declare PL/PgSQL functions as LANGUAGE plpgsql.
If you instead want to use an SQL function, use a CASE expression instead of IF.
CREATE FUNCTION test2() RETURNS integer LANGUAGE sql
AS $$ SELECT CASE WHEN TRUE THEN 1 ELSE 0 END; $$;
(Note, I haven't bothered to format these readably. Don't write functions all in one line like this in real code.)

Using script variables in sql-do session not works?

It is not a psql terminal, I can't use \set myvariable value as suggested here, and can't use ugly set+current_setting() as here ... I am using Pg9+ (9.5), so I think I can use sql-do as this in answer... But not working.
DO $$
DECLARE foo int := bar('etc');
BEGIN
SELECT run_somthing(foo,1);
SELECT run_somthing(foo,2);
END
$$;
ERROR-1: "query has no destination for the resulting data HINT: If you want to discard the results of a SELECT, use PERFORM."
ERROR-2: (after add PERFORM) "ERROR: syntax error at or near "SELECT"".
NOTE:
Also sintax error when using function workaround
CREATE or replace FUNCTION doThis() RETURNS int AS $$
DECLARE
foo int := bar('etc');
BEGIN
SELECT run_somthing(foo,1); -- bug also with PERFORM clause
SELECT run_somthing(foo,2);
RETURN 1;
END;
$$ LANGUAGE plpgsql;
SELECT doThis();

Passing table names in an array

I need to do the same deletion or purge operation (based on several conditions) on a set of tables. For that I am trying to pass the table names in an array to a function. I am not sure if I am doing it right. Or is there a better way?
I am pasting just a sample example this is not the real function I have written but the basic is same as below:
CREATE OR REPLACE FUNCTION test (tablename text[]) RETURNS int AS
$func$
BEGIN
execute 'delete * from '||tablename;
RETURN 1;
END
$func$ LANGUAGE plpgsql;
But when I call the function I get an error:
select test( {'rajeev1'} );
ERROR: syntax error at or near "{"
LINE 10: select test( {'rajeev1'} );
^
********** Error **********
ERROR: syntax error at or near "{"
SQL state: 42601
Character: 179
Array syntax
'{rajeev1, rajeev2}' or ARRAY['rajeev1', 'rajeev2']. Read the manual.
TRUNCATE
Since you are deleting all rows from the tables, consider TRUNCATE instead. Per documentation:
Tip: TRUNCATE is a PostgreSQL extension that provides a faster
mechanism to remove all rows from a table.
Be sure to study the details. If TRUNCATE works for you, the whole operation becomes very simple, since the command accepts multiple tables:
TRUNCATE rajeev1, rajeev2, rajeev3, ..
Dynamic DELETE
Else you need dynamic SQL like you already tried. The scary missing detail: you are completely open to SQL injection and catastrophic syntax errors. Use format() with %I (not %s to sanitize identifiers like table names. Or, better yet in this particular case, use an array of regclass as parameter instead:
CREATE OR REPLACE FUNCTION f_del_all(_tbls regclass)
RETURNS void AS
$func$
DECLARE
_tbl regclass;
BEGIN
FOREACH _tbl IN ARRAY _tbls LOOP
EXECUTE format('DELETE * FROM %s', _tbl);
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT f_del_all('{rajeev1,rajeev2,rajeev3}');
Explanation here:
Table name as a PostgreSQL function parameter
You used wrong syntax for text array constant in the function call. But even if it was right, your function is not correct.
If your function has text array as argument you should loop over the array to execute query for each element.
CREATE OR REPLACE FUNCTION test (tablenames text[]) RETURNS int AS
$func$
DECLARE
tablename text;
BEGIN
FOREACH tablename IN ARRAY tablenames LOOP
EXECUTE FORMAT('delete * from %s', tablename);
END LOOP;
RETURN 1;
END
$func$ LANGUAGE plpgsql;
You can then call the function for several tables at once, not only for one.
SELECT test( '{rajeev1, rajeev2}' );
If you do not need this feature, simply change the argument type to text.
CREATE OR REPLACE FUNCTION test (tablename text) RETURNS int AS
$func$
BEGIN
EXECUTE format('delete * from %s', tablename);
RETURN 1;
END
$func$ LANGUAGE plpgsql;
SELECT test('rajeev1');
I recommend using the format function.
If you want to execute a function (say purge_this_one_table(tablename)) on a group of tables identified by similar names you can use this construction:
create or replace function purge_all_these_tables(mask text)
returns void language plpgsql
as $$
declare
tabname text;
begin
for tabname in
select relname
from pg_class
where relkind = 'r' and relname like mask
loop
execute format(
'purge_this_one_table(%s)',
tabname);
end loop;
end $$;
select purge_all_these_tables('agg_weekly_%');
It should be:
select test('{rajeev1}');

How to use EXECUTE FORMAT ... USING in postgres function

CREATE OR REPLACE FUNCTION dummytest_insert_trigger()
RETURNS trigger AS
$BODY$
DECLARE
v_partition_name VARCHAR(32);
BEGIN
IF NEW.datetime IS NOT NULL THEN
v_partition_name := 'dummyTest';
EXECUTE format('INSERT INTO %I VALUES ($1,$2)',v_partition_name)using NEW.id,NEW.datetime;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION dummytest_insert_trigger()
OWNER TO postgres;
I'm trying to insert using
insert into dummyTest values(1,'2013-01-01 00:00:00+05:30');
But it's showing error as
ERROR: function format(unknown) does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Context: PL/pgSQL function "dummytest_insert_trigger" line 8 at EXECUTE statement
I'm unable get the error.
Your function could look like this in Postgres 9.0 or later:
CREATE OR REPLACE FUNCTION dummytest_insert_trigger()
RETURNS trigger AS
$func$
DECLARE
v_partition_name text := quote_ident('dummyTest'); -- assign at declaration
BEGIN
IF NEW.datetime IS NOT NULL THEN
EXECUTE
'INSERT INTO ' || v_partition_name || ' VALUES ($1,$2)'
USING NEW.id, NEW.datetime;
END IF;
RETURN NULL; -- You sure about this?
END
$func$ LANGUAGE plpgsql;
About RETURN NULL:
To ignore result in BEFORE TRIGGER of PostgreSQL?
I would advice not to use mixed case identifiers. With format( .. %I ..) or quote_ident(), you'd get a table named "dummyTest", which you'll have to double quote for the rest of its existence. Related:
Are PostgreSQL column names case-sensitive?
Use lower case instead:
quote_ident('dummytest')
There is really no point in using dynamic SQL with EXECUTE as long as you have a static table name. But that's probably just the simplified example?
You need explicit cast to text:
EXECUTE format('INSERT INTO %I VALUES ($1,$2)'::text ,v_partition_name) using NEW.id,NEW.datetime;