You want a single round trip to the server with a pure SQL solution.
There were many related requests over the course of the years. It's not possible on principle.
- You either need dynamic SQL - which requires a function using PL/pgSQL or another PL.
- Or you need two round trips to the server. First check existence, then query.
You also cannot nest this in a plain SQL function, which would plan every statement in the body before execution and fail while trying to resolve a non-existing table name. It would not even pass the superficial tests at function creation time. See:
Two simple solutions for your simple case (among many possible ways):
CREATE OR REPLACE FUNCTION f_select_version_if_exists1()
RETURNS int LANGUAGE plpgsql PARALLEL SAFE AS
$func$
BEGIN
IF EXISTS (
SELECT FROM pg_catalog.pg_tables
WHERE tablename = 'versions'
AND schemaname = 'public' -- see below!
) THEN
RETURN (SELECT version FROM versions LIMIT 1);
ELSE
RETURN 0;
END IF;
END
$func$;
Or:
CREATE OR REPLACE FUNCTION f_select_version_if_exists2(INOUT _version int = 0) AS
$func$
BEGIN
IF EXISTS (
SELECT FROM pg_catalog.pg_tables
WHERE tablename = 'versions'
AND schemaname = 'public' -- see below!
) THEN
SELECT INTO _version version
FROM versions LIMIT 1;
END IF;
END
$func$ LANGUAGE plpgsql PARALLEL SAFE;
I highly recommend to also pin down the schema name. Table names are not unique in Postgres. See:
Also, I made the function PARALLEL SAFE
(which only matters if it might be nested in big queries). This would be wrong while relying on the search_path
because that typically includes the schema for temporary objects, which makes it PARALLEL UNSAFE
The second is making creative use of an INOUT
parameter. Related: