Table name as a PostgreSQL function parameter
Before you go there: for only few, known tables names, it's typically simpler to avoid dynamic SQL and spell out the few code variants in separate functions or in a CASE
construct.
That said, what you are trying to achieve can be simplified and improved:
CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
INTO result;
END
$func$;
Call with schema-qualified name (see below):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Or:
SELECT some_f('"my very uncommon table name"');
Major points
Use an OUT
parameter to simplify the function. You can directly select the result of the dynamic SQL into it and be done. No need for additional variables and code.
EXISTS
does exactly what you want. You get true
if the row exists or false
otherwise. There are various ways to do this, EXISTS
is typically most efficient.
You seem to want an integer back, so I cast the boolean
result from EXISTS
to integer
, which yields exactly what you had. I would return boolean instead.
I use the object identifier type regclass
as input type for _tbl
. That does everything quote_ident(_tbl)
or format('%I', _tbl)
would do, but better, because:
.. it prevents SQL injection just as well.
.. it fails immediately and more gracefully if the table name is invalid / does not exist / is invisible to the current user. (A
regclass
parameter is only applicable for existing tables.).. it works with schema-qualified table names, where a plain
quote_ident(_tbl)
orformat(%I)
would fail because they cannot resolve the ambiguity. You would have to pass and escape schema and table names separately.
It only works for existing tables, obviously.
I still use format()
, because it simplifies the syntax (and to demonstrate how it's used), but with %s
instead of %I
. Typically, queries are more complex so format()
helps more. For the simple example we could as well just concatenate:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
No need to table-qualify the id
column while there is only a single table in the FROM
list. No ambiguity possible in this example. (Dynamic) SQL commands inside EXECUTE
have a separate scope, function variables or parameters are not visible there - as opposed to plain SQL commands in the function body.
Here's why you always escape user input for dynamic SQL properly:
db<>fiddle here demonstrating SQL injection
Old sqlfiddle
Pass table to postgreSQL function, execute select statement, return table
You can not pass a "table" as a function argument. You can pass the name of a table to a PL/pgSQL function and then run a dynamic query:
CREATE OR REPLACE FUNCTION summing_function(input_table text)
RETURNS TABLE (col_1 int, col_2 int, col_3 int, col_4 int) AS $$
BEGIN
RETURN QUERY EXECUTE
format('SELECT *, col_1 + col_2 + col_3 FROM %I', input_table);
END;
$$ LANGUAGE plpgsql;
How do I pass in a table parameter to this function?
Everything tested in Postgres 9.4.
Postgres has some weak spots in the syntax for handling ROW types. You cannot cast from a table (alias) directly:
SELECT w::waypoint FROM waypoints w;
ERROR: cannot cast type waypoints to waypoint
The solution is only one step away: decompose the row in a subquery, then the cast works. This way, column values are decomposed and wrapped into the new type directly, without casting to text
and back. No need to list all columns individually and you don't need to create a custom cast, either:
SELECT (w.*)::waypoint FROM (SELECT * FROM waypoints) w;
Or shorter:
SELECT w.*::waypoint FROM (TABLE waypoints) w;
Or shorter, yet:
SELECT w::waypoint FROM (TABLE waypoints) w;
- Is there a shortcut for SELECT * FROM?
SQL Fiddle
That's shorter and faster, in a quick test with 30k rows and simple types 10x faster than casting to text
and back. If you have (big) jsonb
columns or any complex type (expensive conversion to/from text
), the difference will be much bigger, yet.
More importantly, you don't need another custom composite (ROW) type. Every table already has its row defined as type automatically. Just use the existing type waypoints
instead of waypoint
(if at all possible). Then all you need is:
SELECT w FROM waypoints w;
Or, for your example:
SELECT everything(t) FROM temp t; -- using type waypoints
SELECT everything(t::waypoint) FROM (TABLE temp) t; -- using type waypoint
Asides:
- A table does not have "arguments" but columns.
You are not passing a
table parameter to this function
, but rather a row value. That's how you pass a table by name:- Table name as a PostgreSQL function parameter
You can't "pass a whole table" as parameter directly in Postgres, there are not table variables. You would use a cursor or a temp table for that.
Function
Your function has an invalid type declaration and is needlessly complex. I seriously doubt you want to create a view:
CREATE FUNCTION everything(_wp waypoint) -- or use type waypoints
RETURNS TABLE(node int, xy text[]) AS
$func$
BEGIN
RETURN QUERY
SELECT ...
END
$func$ LANGUAGE plpgsql;
text array
is not valid syntax, using text[]
instead to declare an array of text
.
Rather not use the table / type name waypoints
as function parameter name, that opens you up to confusing errors.
Or just use a simple SQL function if your case is as simple as demonstrated:
CREATE FUNCTION everything(_wp waypoint) -- or use type waypoints
RETURNS TABLE(node int, xy text[]) AS
$func$
SELECT ...
$func$ LANGUAGE sql;
Don't quote the language name. It's an identifier.
How to select from a Postgresql function that requires a table as argument
This work for me:
SELECT (func(t.*)).* FROM tab1 AS t WHERE t.id = 1
You could use LATERAL JOIN
too:
SELECT t.*
FROM tab1,
LATERAL func(tab1) AS t
WHERE tab1.id = 1;
Passing table name to a function and returning a table
If a function returns a scalar value it must contain return <expression>
:
- declare a variable,
- assign a value to it (in the example with
execute ... into ...
), - use it in
return <expression>
.
Example:
drop function if exists foo(_t text);
create or replace function foo(_t text)
returns bigint as
$func$
declare
res bigint;
begin
execute format('select count(*) from %I', _t) into res;
return res;
end
$func$ language plpgsql;
select foo('films');
If a function returns a table it must contain return next
or return query
:
- assign a value to a column of declared return table,
- use
return next
, - call the function in FROM clause, not in SELECT list.
Example 1:
drop function if exists foo(_t text);
create or replace function foo(_t text)
returns table(count bigint) as
$func$
begin
execute format('select count(*) from %I', _t) into count;
return next;
end
$func$ language plpgsql;
select * from foo('films');
If a function returns a table you can also use return query
:
Example 2:
drop function if exists foo(_t text);
create or replace function foo(_t text)
returns table(count bigint) as
$func$
begin
return query
execute format('select count(*) from %I', _t);
end
$func$ language plpgsql;
select * from foo('films');
Can a function taking a table as parameter be declared as IMMUTABLE?
If you are concerned about performance, you should make that a language sql
function so it can be inlined
CREATE FUNCTION isvisible(p_row clients)
RETURNS BOOLEAN
AS $BODY$
select p_row.active AND NOT p_row.deceased;
$BODY$
LANGUAGE sql
STABLE;
Even if it's not inlined, a SQL function is still more efficient then a PL/pgSQL function (see e.g. here)
As pointed out by Laurenz Albe in the comments, it is safe to mark that function immutable.
Postgresql function with values (from another table) as arguments
Enclose the SELECT statements in parentheses to make them expressions like this:
select * from interval_generator(
(SELECT datetime FROM lookup Order By datetime limit 1),
(SELECT datetime FROM lookup Order By datetime Desc limit 1),
'1 hours'::interval
);
Please note that
SELECT datetime FROM lookup Order By datetime limit 1
is exactly
SELECT min(datetime) FROM lookup
which seems to me better readable. As the function body of interval_generator
comprises of a single SQL query why don't you make it a plain SQL function instead of pl/pgsql?
<your-function-declaration> as $$
SELECT
(n) time_start,
(n + round_interval) time_end
FROM generate_series(date_trunc('minute', dt_start), dt_end, round_interval) n;
$$
LANGUAGE 'sql';
Related Topics
Syntax Error When Using Row_Number in SQLite3
Delete Primary Key Row Which Is Foreign Key of Another Table
Optimizing My MySQL Statement! - Rand() Too Slow
Alter Column Datatype with Primary Key
SQL Group Rows with Same Value, and Put That Value into Header
Postgresql Reusing Computation Result in Select Query
Ms Access 2010: "Cannot Open Any More Databases."
How to Check the Query Is Using Index
Is Order by and Row_Number() Deterministic
Insert Multiple Rows in One Table Based on Number in Another Table
Complex Sort of Field "String - Number - String"
SQL Server 2005 Restore One Schema Only
Select Unique Column Names from a List of Joined Tables
Why Can't I Refer to a Column Alias in the Order by Using Case