Write like and not like dynamically in plpgsql - sql

SQL1:
select regno from student where regno **like 'ABCD%'**
This is running successfully. But how can I write like 'ABCD%' dynamically?
For example:
CREATE OR REPLACE FUNCTION check_regno(refcursor, character varying)
RETURNS refcursor AS
$BODY$
begin
select regno from student where regno $1
return $1;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Now I want to pass $1 as like 'ABCD%' i.e.:
select check_regno(f1, "like 'ABCD%'")
This will give error at $1:
Please suggest how to achieve this.

As #Igor mentioned, this is error prone. I would go one step further: Don't do it. You invite SQL injection. Consider this related answer on dba.SE.
In fact, I don't see anything in your question warranting dynamic SQL at all. Use a plain SQL function and pass a plain string value instead:
CREATE OR REPLACE FUNCTION check_regno(_like bool, _filter text)
RETURNS SETOF text AS
$func$
SELECT regno FROM student
WHERE CASE WHEN $1 THEN regno ~~ $2 ELSE regno !~~ $2 END
$func$ LANGUAGE sql;
~~ and !~~ being Postgres operators for LIKE and NOT LIKE (you can use either).
Call:
SELECT * FROM check_regno(TRUE, 'ABCD%');
SELECT * FROM check_regno(FALSE, 'DEFG%');

Try something like:
CREATE OR REPLACE FUNCTION check_regno(p_filter varchar)
RETURNS SETOF student.regno%TYPE AS
$BODY$
begin
return query execute 'select regno from student where regno'||p_filter;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
SELECT * FROM check_regno('like ''ABCD%''');
But this type of dynamic SQL is error prone and can allow SQL injections.

Related

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()?

PostgreSQL: No function matches the given name and argument types

I have the following procedure:
DROP FUNCTION presAdress();
CREATE FUNCTION presadress() RETURNS VARCHAR(100) AS $$
DECLARE studioName text;
BEGIN
RETURN (
SELECT address AS pres_address
FROM MovieExec
WHERE cert# IN (
SELECT presC#
FROM Studio
WHERE name = studioName)
);
END;
$$ LANGUAGE plpgsql;
I try to run the procedure:
select presadress('Paramount');
But I get the following error message:
ERROR: function presadress(text) does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Character: 294
I suspect that this is because there is some kind of error regarding the in parameters of the procedure, but I have been unable to find a solution.
Use a function parameter, like #Gordon demonstrates, but you don't need plpgsql for this at all. And the query can simplified (shorter, faster):
CREATE FUNCTION presadress(_studioname text)
RETURNS text AS
$$
SELECT m.address
FROM studio s
JOIN movieexec m ON m.cert# = s.presc#
WHERE s.name = _studioname
$$ LANGUAGE sql STABLE;
Function volatility can be STABLE.
Related:
How do IMMUTABLE, STABLE and VOLATILE keywords effect behaviour of function?
Difference between language sql and language plpgsql in PostgreSQL functions
PostgreSQL Stored Procedure Performance
I think you want a declaration more like this:
CREATE FUNCTION presadress (v_studioName text)
RETURNS VARCHAR(100) AS $$
BEGIN
RETURN(SELECT address AS pres_address
FROM MovieExec
WHERE cert# IN (SELECT presC#
FROM Studio
WHERE name = v_studioName)
);
END;
$$ LANGUAGE plpgsql;

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.

Function with SQL query has no destination for result data

I am trying to create a function that returns a SELECTed resultset.
When I call my postgres function like this select * from tst_dates_func() I get an error as shown below:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "tst_dates_func" line 3 at SQL statement
********** Error **********
ERROR: query has no destination for result data
SQL state: 42601
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Context: PL/pgSQL function "tst_dates_func" line 3 at SQL statement
Here is the function I created:
CREATE OR REPLACE FUNCTION tst_dates_func()
RETURNS TABLE( date_value date, date_id int, date_desc varchar) as
$BODY$
BEGIN
select a.date_value, a.date_id, a.date_desc from dates_tbl a;
END;
$BODY$
LANGUAGE plpgsql;
I am not sure why I am getting the above error. I would like to run select * from tst_dates_func();
and get data back. Or further join the result set if needed. What is the problem here?
Do it as plain SQL
CREATE OR REPLACE FUNCTION tst_dates_func()
RETURNS TABLE( date_value date, date_id int, date_desc varchar) as
$BODY$
select a.date_value, a.date_id, a.date_desc from dates_tbl a;
$BODY$
LANGUAGE sql;
If you really need plpgsql use return query
CREATE OR REPLACE FUNCTION tst_dates_func()
RETURNS TABLE( date_value date, date_id int, date_desc varchar) as
$BODY$
BEGIN
perform SELECT dblink_connect('remote_db');
return query
select a.date_value, a.date_id, a.date_desc from dates_tbl a;
END;
$BODY$
LANGUAGE plpgsql;
In PLPGSQL - use RETURN QUERY
CREATE OR REPLACE FUNCTION tst_dates_func()
RETURNS TABLE( date_value date, date_id int, date_desc varchar) as
$BODY$
BEGIN
RETURN QUERY (select a.date_value, a.date_id, a.date_desc from dates_tbl a);
END;
$BODY$
LANGUAGE plpgsql;
I couldn't do it as plain SQL as I needed to enter some data into a database for further processing and wanted to create a variable. Or at least I did not figure out the correct syntax for that. And the accepted answer had code I did not need, such as connecting to the database, as I ran this from inside pgAdmin with a connection setup already. I also had to drop the function when I made edits to it.
I was using this for inserting a geometry for intersection. A different use case and example could help someone else. This also shows how to then view this data and use it just like a table.
-- Get a geojson shape inside of postgres for further use
DROP FUNCTION fun();
CREATE OR REPLACE FUNCTION fun()
RETURNS TABLE (geometry geometry) AS
$BODY$
DECLARE geojson TEXT;
BEGIN
geojson := '{
"type":"Polygon",
"coordinates":[[[-90.9516399548092,39.8942337977775],[-90.9513913202472,39.8936939306154],[-90.9522805177147,39.8937108246505],[-90.9549542293894,39.8937616571416],[-90.954948768846,39.8945506794343],[-90.9531755591848,39.894492766522],[-90.9531770788457,39.8942868819087],[-90.9516399548092,39.8942337977775]]],
"crs":{"type":"name","properties":{"name":"EPSG:4326"}},
}';
return query (SELECT ST_GeomFromGeoJSON(geojson) AS geometry);
END;
$BODY$
LANGUAGE plpgsql;
-- View test insert
SELECT * FROM fun()

Postgres Dynamic Query Function

I need to create a function that will run a query and return the results with the table name and the column name being arugments given to the function. I currently have this:
CREATE OR REPLACE FUNCTION qa_scf(tname character varying, cname character varying)
RETURNS SETOF INT AS
$BODY$
BEGIN
RETURN QUERY SELECT * FROM tname WHERE cname !='AK' AND cname!='CK';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
This gives me the error "Relation 'tname' des not exist" when run. I'm new to function creating for Postgres, so any help is appreciated. I feel like the return int is wrong, but I don't know what else to put to make it return all columns for the rows returned. Thanks!
You cannot use a variable in place of an identifier like that. You need to do it with dynamic queries. It will look something like this:
EXECUTE 'SELECT * FROM ' || quote_ident(tname)
|| ' WHERE ' || quote_ident(cname) || ' NOT IN (''AK'',''CK'');'
INTO result_var;
If you are using PostgreSQL 9.1 or above, you can use the format() function which makes constructing this string much easier.
Table and column names can not be specified as parameters or variables without dynamically constructing a string to execute as a dynamic statement. Postgres has excellent introductory documentation about executing dynamic statements. It's important to properly quote identifiers and literals with quote_ident() or quote_literal(). The format() function helps clean up dynamic sql statement construction. Since you declare the function to return SETOF INTEGER, you should select the integer field you want, not *.
CREATE OR REPLACE FUNCTION qa_scf(tname text, cname text)
RETURNS SETOF INTEGER AS
$BODY$
BEGIN
RETURN QUERY EXECUTE format(
'SELECT the_integer_field FROM %I WHERE %I NOT IN (%L, %L)',
tname, cname, 'AK', 'CK'
);
END;
$BODY$
LANGUAGE plpgsql;