Format specifier for integer variables in format()

2019-01-19 23:44发布

问题:

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?

回答1:

This would be shorter, faster and safer:

CREATE OR REPLACE FUNCTION get_parent_ltree(parent_id bigint, tbl_name regclass
                                          , OUT parent_ltree ltree) AS
$func$
BEGIN

EXECUTE format('SELECT l_tree FROM %s WHERE id = $1', tbl_name)
INTO  parent_ltree
USING parent_id;

END
$func$ LANGUAGE plpgsql;

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. There is an even better way, though: use a regclass object-identifier type. Details here:
    Table name as a PostgreSQL function parameter

  • Use an OUT parameter to simplify your code. Performance is the same.

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



回答2:

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