I am using PostgreSQL 11 version.
I want to implement a function that takes the layer name (table), column name, and id as parameters.
create or replace function test(layer_name anyelement, field_name anyelement, object_id text)
returns setof anyelement
language plpgsql
as $function$
begin
return query execute format('
select
*
from
%s
where
%s = cast($1 as int4)'
, pg_typeof(layer_name), pg_typeof(field_name))
using object_id;
end;
$function$
;
This is the code I've implemented and when I call the function I get an error.
What am I doing wrong?
Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "="
Where: PL/pgSQL function test(anyelement,anyelement,text) line 3 at RETURN QUERY
### The error occurred while setting parameters
### SQL: select * from test(?, ?, ?)
### Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "="
Where: PL/pgSQL function test(anyelement,anyelement,text) line 3 at RETURN QUERY
; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: ERROR: syntax error at or near "="
Where: PL/pgSQL function test(anyelement,anyelement,text) line 3 at RETURN QUERY}
org.springframework.jdbc.BadSqlGrammarException:
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "="
Where: PL/pgSQL function test(anyelement,anyelement,text) line 3 at RETURN QUERY
### The error occurred while setting parameters
### SQL: select * from test(?, ?, ?)
### Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "="
Where: PL/pgSQL function test(anyelement,anyelement,text) line 3 at RETURN QUERY
; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: ERROR: syntax error at or near "="
Where: PL/pgSQL function test(anyelement,anyelement,text) line 3 at RETURN QUERY
You have to change your function slightly
Instead of field_name anyelement use field_name text in parameter.
and in place of pg_typeof(field_name) use only field_name:
So your function definition will be:
create or replace function test(layer_name anyelement, field_name text, object_id text)
returns setof anyelement
language plpgsql
as $function$
begin
return query execute format('
select
*
from
%s
where
%s = cast($1 as int4)'
, pg_typeof(layer_name), field_name)
using object_id;
end;
$function$
;
Most important part is calling of the function:
select * from test(null::table_name,'field_name','2');
Please note that your field_name always should be integer type and object_id should be number only because you are casting it to integer.
DEMO
Related
I'm learning PosgreSQL, i'm trying to call function get_color in a SELECT statement inserting the column name of the table as parameter, but PostgreSQL version 9.6 (i've tried on 13.0 with the same result) returns me an UndefinedFunction error.
Here's the complete query code:
SELECT n
FROM pianokeys pn,
LATERAL get_color(pn.n) AS res;
CREATE OR REPLACE FUNCTION get_color(n integer) RETURNS text AS $$
BEGIN
IF((n % 88) % 2)
THEN
RETURN $b$black$b$;
ELSE
RETURN $w$white$w$;
END IF;
END;
$$
LANGUAGE plpgsql;
`
Here's the compiler error:
There was an error with the SQL query:
PG::UndefinedFunction: ERROR: function get_color(integer) does not exist
LINE 8: lateral get_color(pn.n) as res) AS "t1" LIMIT 1
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
What am i missing?
Using PostgreSQL 12.3, I am having some trouble trying to validate this simple chunk of plpgsql code
create or replace function test()
returns void
as $$
begin
prepare plan as select 1;
execute plan;
end;
$$ language plpgsql;
Error is
Unterminated dollar-quoted string at or near "$$ begin prepare plan as select 1;"
I have tried with and without ; after end. I have also tried with sql instead of plpgsql.
Any idea of whats is wrong?
This is a db-fiddle to quickly test the code:
https://www.db-fiddle.com/f/KgRZcxXqJs2Lwe284Mj5y/3
The issue is not with the $$ quoting:
create or replace function test()
returns void
as $$
begin
prepare plan as select 1;
execute plan;
end;
$$ language plpgsql;
CREATE FUNCTION
select test();
ERROR: column "plan" does not exist
LINE 1: SELECT plan
^
QUERY: SELECT plan
CONTEXT: PL/pgSQL function test() line 4 at EXECUTE
When you run this in the dbfiddle the full error output is:
Schema Error: error: unterminated dollar-quoted string at or near "$$ begin prepare plan as select 1;"
Schema Error: error: prepared statement "plan" does not exist
Schema Error: error: unterminated dollar-quoted string at or near "$$ language plpgsql;"
Query Error: error: function test() does not exist
The issue is that EXECUTE inside plpgsql is its own command:
https://www.postgresql.org/docs/12/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN
EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ];
I would use the plpgsql form. This works:
create or replace function test()
returns void
as $$
begin
prepare plan as select 1;
EXECUTE 'execute plan';
RAISE NOTICE 'Made it';
DEALLOCATE plan;
end;
$$ language plpgsql;
select test();
NOTICE: Made it
test
------
(1 row)
I am trying to get values from a table into an array and the array definition is different from the table's column definition. I have tried do cast but it is not working. Basically I need as an array (tab_small_str) the values in the table. Can someone please suggest on it:
CREATE TYPE tab_small_str AS (
str CHARACTER VARYING(50)
);
create table test_emp(emp_id integer, ename character varying (10));
insert into test_emp values(1,'a1')
insert into test_emp values(2,'a2')
insert into test_emp values(3,'a3')
CREATE OR REPLACE function test_fn () RETURNS VARCHAR[] as
$$
DECLARE
v_ename tab_small_str[];
i tab_small_str;
BEGIN
SELECT ARRAY(SELECT ename::tab_small_str FROM test_emp) INTO v_ename;
RAISE INFO 'array is: %',v_ename;
RETURN v_ename;
FOREACH i IN ARRAY v_ename
LOOP
RAISE info 'value of ename is%', i;
END LOOP;
END;
$$
language plpgsql;
(function compiles fine).
select test_fn()
--gives below error
ERROR: malformed record literal: "a1"
DETAIL: Missing left parenthesis.
CONTEXT: SQL statement "SELECT ARRAY(SELECT ename::tab_small_str FROM test_emp)"
PL/pgSQL function test_fn() line 7 at SQL statement
********** Error **********
ERROR: malformed record literal: "a1"
SQL state: 22P02
Detail: Missing left parenthesis.
Context: SQL statement "SELECT ARRAY(SELECT ename::tab_small_str FROM test_emp)"
PL/pgSQL function test_fn() line 7 at SQL statement
Hi 404,
i modified as suggested:
CREATE OR REPLACE function test_fn () RETURNS tab_small_str[] as
$$
DECLARE
v_ename tab_small_str[];
i tab_small_str;
BEGIN
SELECT ARRAY(SELECT ROW(ename)::tab_small_str FROM test_emp) INTO v_ename;
RAISE INFO '%',v_ename;
FOREACH i IN ARRAY v_ename
LOOP
RAISE NOTICE '%', i;
END LOOP;
RETURN v_ename;
END;
$$
language plpgsql;
it returns output as:
INFO: {(a1),(a2),(a3)}
CONTEXT: PL/pgSQL function test_fn() line 9 at RAISE
NOTICE: (a1)
CONTEXT: PL/pgSQL function test_fn() line 13 at RAISE
NOTICE: (a2)
CONTEXT: PL/pgSQL function test_fn() line 13 at RAISE
NOTICE: (a3)
CONTEXT: PL/pgSQL function test_fn() line 13 at RAISE
My question is why the output is surrounded by bracket - why not just a1 but (a1). Can you please suggest on it?
Your new type is not a "single field data type", for want of a better description, where you can cast something like a VARCHAR(10) directly to it; it's a ROW containing a single field. So something like 'blah'::tab_small_str fails because it's trying to cast that text to the type which contains a field, rather than the field itself.
To resolve, using your existing query:
SELECT ename::tab_small_str FROM test_emp
Change to:
SELECT ROW(ename)::tab_small_str FROM test_emp
As to why your results are surrounded by brackets: that is how a ROW or composite type is displayed when shown as a single field (or, non-expanded): for example, if you do SELECT * FROM test_emp, the * returns all fields individually as separate columns; however if you do SELECT test_emp FROM test_emp, that will return the table row unexpanded, so it will look like so:
(1,a1)
(2,a2)
(3,a3)
And composite types are exactly the same. i tab_small_str; - think of i as test_emp, which contains fields which can be expanded. In your code you are printing the object i, rather than i.* or i.str. So change your code to:
FOREACH i IN ARRAY v_ename
LOOP
RAISE NOTICE '%', i.str;
END LOOP;
I was wondering if there is a way to syntax an INSERT using EXECUTE in plpgsql.
Suppose I want to have a dynamic insert with mutliple IFs. I try to create something like the following
CREATE FUNCTION __a_inj(creator text) RETURNS integer
AS $query$
DECLARE ii ALIAS FOR $1;
BEGIN
EXECUTE'
INSERT INTO deleteme(name) VALUES($1) RETURNING id'
USING creator;
return ii;
END;
$query$
LANGUAGE plpgsql;
I call it with select __a_inj('drop table deleteme;--'); and get
ERROR: invalid input syntax for integer: "drop table deleteme;--"
CONTEXT: PL/pgSQL function __a_inj(text) while casting return value
to function's return type SQL state: 22P02
If I replace the INSERT line with INSERT INTO deleteme(name) VALUES($1) RETURNING id into ii' I get
ERROR: syntax error at or near "into" LINE 2: ... INSERT INTO
deleteme(name) VALUES($1) RETURNING id into ii
If the function is
CREATE FUNCTION __a_inj(creator text) RETURNS integer
AS $query$
DECLARE ii ALIAS FOR $1;
BEGIN
RETURN EXECUTE'
INSERT INTO deleteme(name) VALUES($1) RETURNING id into ii'
USING creator;
--return ii;
END;
$query$
LANGUAGE plpgsql;
I get
ERROR: syntax error at or near "USING" LINE 8: USING creator;
How can I have a dynamic INSERT with EXECUTE
OR
I guess there is no need to syntax one. Just use ifs to create the INTO and values part and then use a simple INSERT that is also strong against SQL injection attacks ?
Both statements are dynamic SQL (EXECUTE), but the second one has a syntax error (there is no INTO clause in SQL). You get the error in the first statement because the string you insert cannot be converted to an integer.
You are safe from SQL injection if you use the USING clause, because that uses a statement with parameters. SQL injection can only happen if you concatenate a user input string with an SQL statement.
I created a dynamic function. I get a part of table name with dynamically. The function is created successfully. But when I execute the function. I get an error. How can I solve this problem? I call the function with
select * from dwgcould.getlatlngcenter(2000653);
CREATE OR REPLACE FUNCTION dwgcould.getlatlngcenter(IN pro_id integer,
OUT lat_center double precision, OUT lng_center double precision)
AS $$
BEGIN
EXECUTE 'SELECT st_x(st_centroid( st_transform(geom,4326))) as lng_center ,st_y(st_centroid( st_transform(geom,4326))) as lat_center
FROM dwgcould.adpes_v1_' || quote_ident(pro_id) || '_line limit 1';
END;
$$ LANGUAGE plpgsql;
The error code is
ERROR: function quote_ident(integer) does not exist
LINE 2: FROM dwgcould.adpes_v1_' || quote_ident(pro_id) || '_line...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
QUERY: SELECT 'SELECT st_x(st_centroid( st_transform(geom,4326))) as lng_center ,st_y(st_centroid( st_transform(geom,4326))) as lat_center
FROM dwgcould.adpes_v1_' || quote_ident(pro_id) || '_line limit 1'
CONTEXT: PL/pgSQL function dwgcould.getlatlngcenter(integer) line 4 at EXECUTE statement
********** Error **********
ERROR: function quote_ident(integer) 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 dwgcould.getlatlngcenter(integer) line 4 at EXECUTE statement
Also How can I check whether table exist?
better use format, eg:
CREATE OR REPLACE FUNCTION dwgcould.getlatlngcenter(IN pro_id integer,
OUT lat_center double precision, OUT lng_center double precision)
AS $$
BEGIN
if (select count(1) from pg_tables where tablename = format('adpes_v1_%s_line',pro_id)) < 1 then
raise info '%','NO SUCH TABLE!';
return;
end if;
EXECUTE format('SELECT * FROM dwgcould.adpes_v1_%s_line limit 1',pro_id) into lat_center,lng_center;
return;
END;
$$ LANGUAGE plpgsql;
docs