Dollar-quotes failing with JDBC - postgresql

I have to write a function in PLPGSQL but I have problem with the function body quoted with dollar-quoting. Using the first tutorial:
CREATE FUNCTION inc(val integer)
RETURNS integer AS
$BODY$
BEGIN
RETURN val + 1;
END;
$BODY$
LANGUAGE PLPGSQL;
I get an error:
unterminated dollar-quoted string at or near $$
Searching on google I just found it's a JDBC Driver problem but I cannot update it.
So I have tried to change the DELIMITER to remove $$:
DELIMITER ++;
CREATE FUNCTION inc(val integer)
RETURNS integer AS
++BODY++
BEGIN
RETURN val + 1;
END;
++BODY++
LANGUAGE PLPGSQL;
DELIMITER ;
The command doesn't return any error but function doesn't exists when I try to call it:
select inc(4);
What am I missing?

The underlying problem is JDBC's inability to deal with dollar-quotes.
I think this was fixed with JDBC version 9.4.1208 (2016-02-16).
See:
Exceptions when creating a trigger in PostgreSQL 9.1
How to execute plpgsql anonymous block in Oracle SQL Developer?
You can avoid the problem by using plain quotes for the simple case:
CREATE FUNCTION inc(val integer)
RETURNS integer AS
'
BEGIN
RETURN val + 1;
END
' LANGUAGE plpgsql;
See:
What are '$$' used for in PL/pgSQL
Insert text with single quotes in PostgreSQL

Related

splitting characters using function in PostgreSQL

I tried to write function like this in PostgreSQL but I'm getting error like
ERROR: syntax error at or near "elems"
LINE 22: RETURN elems;
I want get output like
input: we###ty;rer##2hjjj
output:
we###ty
rer##2hjjj
please help me to solve this error
CREATE OR REPLACE FUNCTION public.fn_split(
inputstr text,
delimeter text)
RETURNS text[]
LANGUAGE 'plpgsql'
COST 100
VOLATILE SECURITY DEFINER PARALLEL UNSAFE
AS $BODY$
DECLARE
delimeter text;
elems text[];
var text;
arr_len int;
BEGIN
SELECT unnest(string_to_array(inputstr,delimeter))
INTO elems
RETURN elems;
END
$BODY$;
CREATE OR REPLACE FUNCTION public.fn_split(
inputstr text,
delimeter text)
RETURNS text[]
LANGUAGE 'plpgsql'
COST 100
VOLATILE SECURITY DEFINER
AS $BODY$
DECLARE
elems text[];
BEGIN
SELECT string_to_array(inputstr,delimeter) INTO elems;
RETURN elems;
END;
$BODY$;
Now call this function like this
SELECT UNNEST(fn_split('1,2,3',',')) as retval
Above is the screenshot which includes function definition and in the first list the command to call this function
Here is the command that you need to execute in order to call this function in the PostgreSQL query window after the creation of the function.
Your function is defined to return an array, however unnest would turn the result of creating the array into rows of strings. There is also no need to duplicate the parameter definition as local variables in a DECLARE block. And as you don't seem to want to manipulate the created array somehow, there is no need to store it in a local variable.
It seems you just want:
CREATE OR REPLACE FUNCTION public.fn_split(
inputstr text,
delimeter text)
RETURNS text[]
LANGUAGE plpgsql
immutable
AS $BODY$
BEGIN
return string_to_array(inputstr,delimeter);
END
$BODY$;
Or simpler as a SQL function:
CREATE OR REPLACE FUNCTION public.fn_split(
inputstr text,
delimeter text)
RETURNS text[]
LANGUAGE sql
immutable
AS
$BODY$
select string_to_array(inputstr,delimeter);
$BODY$;
Note that the language name is an identifier and should not be enclosed in single quotes. This syntax is deprecated and support for it will be removed in a future Postgres version.
Edit:
It seems you don't actually want an array, but one row per element after splitting the input value. In that case the function should be declared as returns table() not returns text[]
CREATE OR REPLACE FUNCTION public.fn_split(
inputstr text,
delimeter text)
RETURNS table(element text)
LANGUAGE sql
immutable
AS
$BODY$
select unnest(string_to_array(inputstr,delimeter));
$BODY$;
Then use it like this:
select *
from fn_split('we###ty;rer##2hjjj', ';');
Since Postgres 14
select unnest(string_to_array(inputstr,delimeter));
can be simplified to
select string_to_table(inputstr,delimeter);

How to concat two string in postgresql function?

I want a function which will return concated string. I am getting following error after execute this function in postgresql.
CREATE OR REPLACE FUNCTION getTableName ()
RETURNS text AS $$
DECLARE
state_short_name text;
BEGIN
state_short_name := (select lower(state_short_name) from mst_state where state_code in (SELECT substr(entity_code,1,2) FROM shg_detail_share WHERE entity_code = '3420006002001'))
RETURN (CONCAT(state_short_name, '_shg_detail'));
END;
$$ LANGUAGE plpgsql
I expect the output like 'jh_shg_detail' but I am getting error like this
ERROR: syntax error at or near "("
LINE 9: RETURN (CONCAT(state_short_name, '_shg_detail'));
You should use a select into in PL/pgSQL. And to avoid a name clash, don't name variables the same as columns:
CREATE OR REPLACE FUNCTION gettablename()
RETURNS text AS $$
DECLARE
l_state_short_name text;
BEGIN
select lower(state_short_name)
into l_state_short_name
from mst_state
where state_code in (SELECT substr(entity_code,1,2)
FROM shg_detail_share
WHERE entity_code = '3420006002001'));
RETURN CONCAT(state_short_name, '_shg_detail');
END;
$$ LANGUAGE plpgsql;
But you don't need PL/pgSQL for a simple SQL query like that. Your sub-query isn't really necessary as well. You can simplify that to where state_code = '34'
CREATE OR REPLACE FUNCTION gettablename()
RETURNS text
AS $$
select concat(lower(state_short_name), '_shg_detail')
from mst_state
where state_code = '34';
$$
LANGUAGE sql;
Your problem is a missing semicolon at the line with the assignment statement :=.
This makes the line that starts with RETURN (CONCAT a continuation line of the statement, and so you get the syntax error reported in that line.

syntax error when running postgresql function/stored procedure

Trying to perform a set of complex Postgresql DB operations by using function, but even a simple function gets me error:
ERROR: syntax error at or near "text"
LINE 3: tmp text := info;
^
Here is SQL
CREATE or REPLACE FUNCTION createme (info text) RETURNS text AS $$
DECLARE
tmp text := info;
BEGIN
select :tmp
end
$$ LANGUAGE SQL;
Any idea why? Thx!
You procedure is not in SQL language, but it is in plpgsql language.
CREATE or REPLACE FUNCTION createme (info text) RETURNS text AS $$
DECLARE
tmp text := info;
BEGIN
RETURN tmp;
end
$$ LANGUAGE plpgsql;
SELECT :tmp is nonsense in this content. Functions returns a value with command RETURN - it is similar to any other environments.

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;