Using ltree query in plpgsql function as param in PostgreSQL - postgresql

I want to execute query with ltree param in plpgsql function... but i can`t understand how to use quotes in this functions...
CREATE OR REPLACE FUNCTION f_select(BIGINT) RETURNS setof categories AS
$$
DECLARE
s_cat ALIAS FOR $1;
queryText TEXT;
result categories%ROWTYPE;
BEGIN
queryText := 'SELECT * FROM categories WHERE cat_tree ~ \'*.\'|| s_cat::text ||\'.*\'';
FOR result IN EXECUTE queryText
LOOP
RETURN NEXT result;
END LOOP;
RETURN;
END
$$
LANGUAGE plpgsql;
How to do this ???
After executing this code in psql I get error:
ERROR: syntax error at or near "."
LINE 10: ... := 'SELECT * FROM categories WHERE cat_tree ~ \'*.\'|| s_ca...
Final working verison:
CREATE OR REPLACE FUNCTION f_select(BIGINT) RETURNS setof categories AS
$$
DECLARE
s_cat ALIAS FOR $1;
queryText TEXT;
result categories%ROWTYPE;
BEGIN
queryText := 'SELECT * FROM categories WHERE cat_tree ~ ''' || ('*.'|| s_cat::text || '.*')::lquery || '''';
FOR result IN EXECUTE queryText
LOOP
RETURN NEXT result;
END LOOP;
RETURN;
END
$$
LANGUAGE plpgsql;

The problem seems to be misusing the backslash, but anyway putting this query into a text variable shouldn't be needed in the first place.
What about this form:
FOR result IN SELECT * FROM categories WHERE cat_tree ~ '*.'|| s_cat::text || '.*'
LOOP
RETURN NEXT result;
END LOOP;
And if the LOOP just has to return the results, you may just as well avoid it and return the query directly:
RETURN QUERY SELECT * FROM categories WHERE cat_tree ~ '*.'|| s_cat::text || '.*';
EDIT:
Since the operator is ltree ~ lquery and ~ binds tighter than ||, the right operand should be parenthesized and cast to lquery:
RETURN QUERY SELECT * FROM categories
WHERE cat_tree ~ ('*.'|| s_cat::text || '.*')::lquery;

Related

How to resolve error when running dynamic PostreSQL function

I have a function that takes 3 parameters: huc, id_list, and email.
CREATE OR REPLACE FUNCTION my_app.job_batch(
huc text,
input_list text[],
email text
) RETURNS VOID AS
$$
DECLARE
id text;
BEGIN
FOREACH id IN ARRAY input_list LOOP
EXECUTE 'SELECT * FROM my_app.my_funct(
' || huc || '::text,
' || id || '::text,
' || email || '::text)';
END LOOP;
END;
$$
LANGUAGE plpgsql;
When I try to run the function however, it throws an error: ERROR: column "myhu4" does not exist
SELECT * FROM spg_app.append_spg_job_batch('MYHUC4', array['1021', '1025','1026','1027','0701','0702','0703','0708','0709'], 'myemail#gmail.com');
Why is it referring to myhuc4 as a column and why is displaying it in lower case. Is my syntax below to run the function with those 3 parameters incorrect? Note: If I run the below hardcoded version, it runs fine:
DO $$
DECLARE
id_list text[] := array['1021', '1025','1026','1027','0701','0702','0703','0708','0709'];
id text;
BEGIN
FOREACH id in ARRAY id_list LOOP
EXECUTE 'SELECT * FROM my_app.my_funct(
''MYHU4''::text,
' || id || '::text,
''myemail#gmail.com''::text)'
END LOOP;
END;
$$
LANGUAGE plpgsql;
I suggest to use parameters instead of bad practice of stitching strings, as follows:
CREATE OR REPLACE FUNCTION my_app.job_batch(
huc text,
input_list text[],
email text
) RETURNS VOID AS
$$
DECLARE
id text;
BEGIN
FOREACH id IN ARRAY input_list LOOP
execute format ('SELECT * FROM my_app.my_funct($1, $2, $3)')
using huc, id, email;
END LOOP;
END;
$$
LANGUAGE plpgsql;
as shown in official docs https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN

How to declare bound cursor with customized column name in pl/pgsql

I would like to use cursor in a function with the table name as a function variable, a simple example would be a select query through cursor.
From the documentation of PostgreSQL I found that I can use
Declare curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
But when I input
declare curs1 cursor (key integer) for execute 'select ' || quote_ident(colname) || ' from ' || quote_ident(tablename);
It returns ERROR: syntax error at or near "'select '".
On the other hand, if I write the function with refcursor as follows:
CREATE or replace FUNCTION cursor_hw(colname text,tablename text) RETURNS setof text AS $$
declare curs1 refcursor;
BEGIN
open curs1 for execute 'select ' || quote_ident(colname) || ' from ' || quote_ident(tablename);
for x in curs1 loop
return next x;
end loop;
END; $$ LANGUAGE plpgsql;
It will return [42601] ERROR: cursor FOR loop must use a bound cursor variable.
Any help would be appreciated, thanks a lot!
You may prefer a simple FOR record_variable IN EXECUTE <query> instead of OPEN FETCH for dynamic SQL.
CREATE or replace FUNCTION cursor_hw(colname text,tablename text)
RETURNS setof text AS
$$
DECLARE
x RECORD;
BEGIN
FOR x IN execute 'select ' || quote_ident(colname) || ' from '
|| quote_ident(tablename)
LOOP
IF x.first_name like 'D%' THEN
RETURN NEXT x;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
execution
knayak=# select cursor_hw('first_name','employees');
cursor_hw
-----------
Donald
Douglas
David
Diana
Daniel
Den
David
Danielle
David
(9 rows)

PostgreSQL - Function declaration - Dynamic WHERE in QUERY

How to change "EXECUTE sql_string" to "RETURN QUERY" in a function declaration, but there is a where array which must be used.
In other words, how to change this
p_sql := 'SELECT *
FROM tbl
WHERE ' || array_to_string(where_arr, ' AND ');
FOR item IN EXECUTE p_sql
LOOP
RETURN NEXT item;
END LOOP;
to this
RETURN QUERY
SELECT *
FROM tbl
WHERE || array_to_string(where_arr, ' AND '); -- this is the place
I want my editors (phpstorm, notepad++) to see SQL as SQL and not as string.
https://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-RETURNING
return query execute $$
select *
from tbl
where $$ || array_to_string(where_arr, ' and ');

Using variable passed in from psql command line, in function

I can't figure out how to access variables inside my plpgsql function. I'm using postgres 9.5 under Cygwin.
functions.sql
-- this works fine
\echo Recreate = :oktodrop
CREATE OR REPLACE FUNCTION drop_table(TEXT) RETURNS VOID AS
$$
BEGIN
IF EXISTS ( SELECT * FROM information_schema.tables WHERE table_name = $1 ) THEN
-- syntax error here:
IF (:oktodrop == 1 ) THEN
DROP TABLE $1;
END IF;
END IF;
END;
$$
language 'plpgsql';
psql.exe -v oktodrop=1 -f functions.sql
Password:
Recreate = 1
psql:functions.sql:13: ERROR: syntax error at or near ":"
LINE 5: IF (:oktodrop == 1 ) THEN
^
Perhaps I've oversimplified your task (feel free to tell me if that's the case), but why not create the function like this:
CREATE OR REPLACE FUNCTION drop_table(tablename TEXT, oktodrop integer)
RETURNS text AS
$$
DECLARE
result text;
BEGIN
IF EXISTS ( SELECT * FROM information_schema.tables WHERE table_name = tablename ) THEN
IF (oktodrop = 1 ) THEN
execute 'DROP TABLE ' || tablename;
result := 'Dropped';
ELSE
result := 'Not Okay';
END IF;
ELSE
result := 'No such table';
END IF;
return result;
END;
$$
language 'plpgsql';
Then the implementation would be:
select drop_table('foo', 1);
I should also caution that because you have not specified the table_schema field, it's conceivable that your target table exists in another schema, and the actual drop command will fail because it doesn't exist in the default schema.

Variable substitution in PL/pgSQL

I have a select statement that is generated dynamically based on the supplied parameter. The problem is that postgresql always says:
argument of WHERE must be type boolean, not type character varying no matter what the parameter is. Did I miss anything?
CREATE OR REPLACE FUNCTION getuid(name character varying) RETURNS integer AS $$
DECLARE
statement varchar;
uid integer;
BEGIN
IF ($1 = '') THEN
statement := 'TRUE';
statement := CAST(statement AS BOOLEAN);
ELSE
statement := 'users.keywords ILIKE''' || '%' || $1 || '%''';
END IF;
SELECT INTO uid id FROM users WHERE "statement";
RETURN uid;
END;
$$ LANGUAGE plpgsql
You need EXECUTE if you want to generate dynamic commands inside a function. You could also use two different sections:
CREATE OR REPLACE FUNCTION getuid(name character varying) RETURNS integer AS $$
DECLARE
statement varchar;
uid integer;
BEGIN
IF ($1 = '' OR $1 IS NULL) THEN -- section 1
SELECT id INTO uid FROM users;
ELSE -- section 2
SELECT id INTO uid FROM users WHERE users.keywords ILIKE '%' || $1 || '%';
END IF;
RETURN uid;
END;
$$ LANGUAGE plpgsql;
EXECUTE is a PL/pgSQL statement and not SQL statement. So you have to wrap your dynamic query into PL/pgSQL stored procedure.
Be careful about variable substitution and do not forget to use quote_literal() or quote_nullable() when building up you query.
Have look in documentation here: http://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN