8
CREATE OR REPLACE FUNCTION getParentLtree(parent_id bigint, tbl_name varchar) 
  RETURNS ltree AS
$BODY$
DECLARE
   parent_ltree ltree;
BEGIN
-- This works fine:
-- select into parent_ltree l_tree from tbl1 where id = parent_id;

EXECUTE format('select into parent_ltree l_tree from %I
                where id = %I', tbl_name,parent_id);

RETURN parent_ltree;
END;
$BODY$ LANGUAGE plpgsql;

There are 2 issues in above function:

  1. parent_id is integer but it is replaced with quotes? What is the correct format specifier for int variables?
  2. select into does not work with EXECUTE? How can I make above commented query to use table name passed?
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
RAFIQ
  • 905
  • 3
  • 18
  • 32

3 Answers3

18

This would be shorter, faster and safer:

CREATE OR REPLACE FUNCTION get_parent_ltree(parent_id bigint, tbl_name regclass
                                          , OUT parent_ltree ltree)
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE format('SELECT l_tree FROM %s WHERE id = $1', tbl_name)
   INTO  parent_ltree
   USING parent_id;
END
$func$;

Why?

Most importantly, use the USING clause of EXECUTE for parameter values. Don't convert them to text, concatenate and interpret them back. That would be slower and error-prone.

Normally you would use the %I specifier with format() for identifiers like the table name. For existing tables, a regclass object-identifier type may be even better. See:

The OUT parameter makes it simpler. Performance is the same.

Don't use unquoted CaMeL case identifiers like getParentLtree in Postgres. Details in the manual.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • great!, its much better, dont know if 2 answers can be accepted. will use all suggestions, CaMel case was my mistake, came just out of habit I knew postgres will make it lower case and using quotes can only make matters worse. – RAFIQ Mar 27 '14 at 06:20
  • what does $1 mean in your select statement ? – Ronald Das Jul 27 '19 at 09:52
  • 1
    @RonaldDas: It's a parameter for the first value provided with the USING clause. Follow the first link for details. – Erwin Brandstetter Jul 27 '19 at 10:09
  • @ErwinBrandstetter Hey. I try to replicate your answer, but I encountered some issue. Would you like to help me solve it. [link](https://stackoverflow.com/questions/70166528/invalid-input-syntax-for-type-integer-2-2-with-custom-data-type-while-execu) – jian Nov 30 '21 at 08:52
5

Use %s for strings. %I is for identifiers:

select format('select into parent_ltree l_tree from %I  where id = %s', 'tbl1', 1);
                         format                          
---------------------------------------------------------
 select into parent_ltree l_tree from tbl1  where id = 1

http://www.postgresql.org/docs/current/static/functions-string.html#FUNCTIONS-STRING-FORMAT

PL/pgSQL's select into is not the same as Postgresql's select into. Use instead create table as:

create table parent_ltree as 
select l_tree 
from tbl1
where id = 1

http://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-ONEROW

Tip: Note that this interpretation of SELECT with INTO is quite different from PostgreSQL's regular SELECT INTO command, wherein the INTO target is a newly created table. If you want to create a table from a SELECT result inside a PL/pgSQL function, use the syntax CREATE TABLE ... AS SELECT.

To select into a variable from an execute statement:

EXECUTE format('select l_tree from %I where id = %s', tbl_name,parent_id) 
into parent_ltree;

http://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN

Clodoaldo Neto
  • 118,695
  • 26
  • 233
  • 260
  • Thanks for reply, %s is the correct type specifier,works for issue 1, but for issue 2 result is only one value not a set, since id is unique in this case, how can we read it into a variable rather than a table? – RAFIQ Mar 26 '14 at 10:39
  • That works as well, Thanks very much! To summarize your solutions 1. %s for Identifiers 2. into should be used outside format function. – RAFIQ Mar 26 '14 at 11:08
2

Following postgres uses a for loop and variable to creates 10 table starting with prefix 'sbtest' namely sbtest1, sbtest2 ... sbtest10

create_table.sql

do $$
    DECLARE myvar integer;
begin
    for myvar in 1..10 loop
        EXECUTE format('CREATE TABLE sbtest%s (
        id SERIAL NOT NULL,
        k INTEGER NOT NULL,
        c CHAR(120) NOT NULL,
        pad CHAR(60) NOT NULL,
        PRIMARY KEY (id))', myvar);
    end loop;
end; $$

Run it using psql -U user_name -d database_name -f create_table.sql

Example Table  sbtest1 is as
id | k | c | pad
----+---+---+-----
(0 rows)

Table "public.sbtest1" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description --------+----------------+-----------+----------+-------------------------------------+----------+------ --------+------------- id | integer | | not null | nextval('sbtest1_id_seq'::regclass) | plain | | k | integer | | not null | | plain | | c | character(120) | | not null | | extended | | pad | character(60) | | not null | | extended | | Indexes: "sbtest1_pkey" PRIMARY KEY, btree (id) Access method: heap