Here's the function:
CREATE OR REPLACE FUNCTION get_img(ptype text, pid integer, pdpi integer)
RETURNS bytea AS
$BODY$
declare psize char(1); pimg bytea;
begin
select size into psize from dpi_size where dpi in(select max(dpi) from dpi_size where dpi <= pdpi);
select coalesce(psize, 's') into psize;
if ptype = 'cat' then
execute 'select img_' || psize || ' into pimg from cat where id = $1' using pid;
end if;
return pimg;
end; $BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION get_img(text, integer, integer)
OWNER TO postgres;
Table cat has img_s, img_m, img_l and an id. Here's what I get when I run select.
select handyman_get_img ('cat', 11, 320)
ERROR: relation "pimg" already exists CONTEXT: SQL statement "select
img_l into pimg from cat where id = $1" PL/pgSQL function
get_img(text,integer,integer) line 8 at EXECUTE statement
Can anyone enlighten me why it says 'pimg' already exists?
Thanks.
You can't use a variable inside the string literal for execute.
The string passed to execute is run "as is" and select .. into pimg from ... is an old (non-standard) syntax that does the same as create table pimg as select ....
If you want to store the result of an execute into a variable you need to use a different syntax:
execute 'select img_' || psize || ' from cat where id = $1'
into pimg
using pid;
Note how the into is outside of the string that is being executed.
Related
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
I am new to creating functions in postgresql. The version that I'm using is rather old. It's 8.2.15 (not my choice, but my org's). The following example is trying to apply one function to a temp table in another function.
-- First function
create or replace function inner_func(_tbl anyelement)
RETURNS void AS
$$
BEGIN
EXECUTE 'ALTER TABLE ' || _tbl || ' ADD COLUMN d_amount INTEGER';
EXECUTE 'UPDATE ' || _tbl || ' SET d_amount = 2* amount';
RETURN;
END;
$$
LANGUAGE plpgsql volatile;
-- Second function
CREATE OR REPLACE FUNCTION outer_func()
RETURNS void AS
$$
BEGIN
DROP TABLE IF EXISTS my_temp;
CREATE TEMP TABLE my_temp
(id serial primary key,
amount integer
);
INSERT into my_temp (amount) values (10),(20);
-- now apply the inner_func right here
EXECUTE 'SELECT inner_func(' || quote_ident('my_temp') || ')';
RETURN;
END;
LANGUAGE plpgsql volatile;
When I run
SELECT outer_func();
It spits out an ERROR:
column "my_temp" does not exist
But the inner_func works if I use it on its own like the following:
create temp table my_temp2
(id serial primary key,
amount integer
);
INSERT INTO my_temp2 (amount) values (10),(20);
SELECT inner_func(quote_ident('my_temp2'));
SELECT * from my_temp2;
id amount d_amount
1 10 20
2 20 40
How can I make this inner_func work inside outer_func? Any idea?
It looks like the problem is here:
EXECUTE 'SELECT inner_func(' || quote_ident('my_temp') || ')';
=>
EXECUTE 'SELECT inner_func(quote_ident(' || quote_literal('my_temp') || '));';
DBFiddle Demo
Trying to create a function that looks for sequence with particular name if does not exist should create it. Then returns function value of sequence. Part of function that does not seem to be working is where it tests if sequence already exists. Below is code for function
CREATE OR REPLACE FUNCTION public."tenantSequence"(
vtenantid integer,
vtablename character)
RETURNS bigint AS
$BODY$DECLARE
vSeqName character varying;
vSQL character varying;
BEGIN
select ('t' || trim(to_char(vtenantid,'0000')) || vtablename) INTO vSeqName;
if not exists(SELECT 0 FROM pg_class where relkind = 'S' and relname = vSeqName )
then
vSQL := 'create sequence '||vSeqName||';';
execute vSQL;
ELSE
return 0;
end if;
return nextval(vSeqName) * 10000 + vtenantid;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public."tenantSequence"(integer, character)
OWNER TO postgres;
Problem was case as mentioned by a_horse_with_no_name
changed line assign vSeqName to the following
vSeqName := lower('t' || trim(to_char(vtenantid,'0000')) || vtablename);
Now function works as expected.
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.
I'm trying to write a function in PL/PgSQL that have to work with a table it receives as a parameter.
I use EXECUTE..INTO..USING statements within the function definition to build dynamic queries (it's the only way I know to do this) but ... I encountered a problem with RECORD data types.
Let's consider the follow (extremely simplified) example.
-- A table with some values.
DROP TABLE IF EXISTS table1;
CREATE TABLE table1 (
code INT,
descr TEXT
);
INSERT INTO table1 VALUES ('1','a');
INSERT INTO table1 VALUES ('2','b');
-- The function code.
DROP FUNCTION IF EXISTS foo (TEXT);
CREATE FUNCTION foo (tbl_name TEXT) RETURNS VOID AS $$
DECLARE
r RECORD;
d TEXT;
BEGIN
FOR r IN
EXECUTE 'SELECT * FROM ' || tbl_name
LOOP
--SELECT r.descr INTO d; --IT WORK
EXECUTE 'SELECT ($1)' || '.descr' INTO d USING r; --IT DOES NOT WORK
RAISE NOTICE '%', d;
END LOOP;
END;
$$ LANGUAGE plpgsql STRICT;
-- Call foo function on table1
SELECT foo('table1');
It output the following error:
ERROR: could not identify column "descr" in record data type
although the syntax I used seems valid to me. I can't use the static select (commented in the example) because I want to dinamically refer the columns names.
So..someone know what's wrong with the above code?
It's true. You cannot to use type record outside PL/pgSQL space.
RECORD value is valid only in plpgsql.
you can do
EXECUTE 'SELECT $1.descr' INTO d USING r::text::xx;
$1 should be inside the || ,like || $1 || and give spaces properly then it will work.
BEGIN
EXECUTE ' delete from ' || quote_ident($1) || ' where condition ';
END;