0

Can we select inside a PostgreSQL function without using EXECUTE? I'm trying to use quote_ident() to create dynamic SQL but it doesn't work.

CREATE OR REPLACE FUNCTION select_server(p_id text) RETURNS integer AS $$ DECLARE serialnum_value INTEGER; STATEMENT TEXT; BEGIN STATEMENT := 'tbl' || substr($1, 1, 4); SELECT serialnum INTO serialnum_value FROM quote_ident(STATEMENT ) WHERE id = $1; RETURN serialnum; END; 

Does anybody have idea how to select from dynamic table in a PostgreSQL function without using EXECUTE?

2
  • 1) It can not be done without EXECUTE. 2) Why you do not want to use EXECUTE? Commented Oct 2, 2013 at 10:36
  • EXECUTE takes more time, right? My old function using STATIC table, runtime : 30 ms My new function using EXECUTE DYNAMIC table, runtime : 90 ms Commented Oct 2, 2013 at 10:42

2 Answers 2

1

You can't avoid execute here, per igor's comment. The best alternative is to create a function that creates a function, which you can then call as needed. Example:

create function gen_select_server(p_id text) returns boolean as $gen$ begin execute $exec$ create function $exec$ || quote_ident('select_server_' || p_id) || $exec$ returns integer as $body$ begin return serialnum from $exec$ || quote_ident('tbl_' || p_id) || $exec$ where id = $exec$ || quote_literal('id_' || p_id) || $exec$; end; $body$ language plpgsql; $exec$; return true; end; $gen$ language plpgsql; -- usage: select gen_select_server('1234'); -- call this only once; uses execute select * from select_server_1234(); -- no execute here 

As the above highlights, it can get messy with string delimiters. Also note that the above isn't your original function -- it's primarily to illustrate how to quote things properly within the big $exec$ "string" block.

I'd recommend to stick to using execute, that being said. Or using an ORM, for that matter. Maintaining these micro-optimizations is potentially a pain worse than the superficial gain in performance.

Sign up to request clarification or add additional context in comments.

Comments

1

You cannot execute dynamic SQL without EXECUTE in PL/pgSQL. That's what makes it dynamic in the first place.

But you can streamline your function quite a bit:

CREATE OR REPLACE FUNCTION select_server(p_id text, OUT serial_value integer) LANGUAGE plpgsql STRICT AS $func$ BEGIN EXECUTE 'SELECT serialnum FROM tbl' || left($1::text, 4)) || ' WHERE id = $1' USING $1 INTO serial_value; END $func$; 
  • Normally you would have to sanitize any identifier.
    But you do not need quote_ident() in this special case, since the only dynamic component are 4 digits from an integer. Neither SQL injection nor illegal identifiers are possible.

  • Pass the value of p_id as value using the USING clause.

  • Reduce the number of assignments. Those are comparatively expensive in PL/pgSQL. You only need a single SQL statement to do everything.

  • The OUT parameter helps to shorten the syntax.

  • I also made the function STRICT (RETURNS NULL ON NULL INPUT), since it would not make sense with NULL as input.

  • left() is slightly faster than substr().

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.