Exception handling in PL/pgSQL
PL/pgSQL code is always wrapped into a BEGIN ... END
block. That can be inside the body of a DO
statement or a function. Blocks can be nested inside - but they cannot exist outside, don't confuse it with plain SQL.
Each block can optionally contain an EXCEPTION
clause for handling exceptions, but functions that need to trap exceptions are more expensive, so it's best to avoid exceptions a priori. Postgres needs to prepare for the possibility of rolling back to a point in the transaction before the exception happened, similar to an SQL SAVEPOINT
. The manual:
A block containing an EXCEPTION
clause is significantly more
expensive to enter and exit than a block without one. Therefore, don't
use EXCEPTION
without need.
Example:
How to avoid an exception in the example
A DO
statement can't return anything. Create a function that takes table and schema name as parameters and returns whatever you want:
CREATE OR REPLACE FUNCTION f_tbl_value(_tbl text, _schema text = 'public')
RETURNS TABLE (value text)
LANGUAGE plpgsql AS
$func$
DECLARE
_t regclass := to_regclass(_schema || '.' || _tbl);
BEGIN
IF _t IS NULL THEN
value := ''; RETURN NEXT; -- return single empty string
ELSE
RETURN QUERY EXECUTE
'SELECT value FROM ' || _t; -- return set of values
END IF;
END
$func$;
Call:
SELECT * FROM f_tbl_value('my_table');
Or:
SELECT * FROM f_tbl_value('my_table', 'my_schema');
Assuming you want a set of rows with a single text
column or an empty string if the table does not exist.
Also assuming that a column value
exists if the given table exists. You could test for that, too, but you didn't ask for that.
Both input parameters are only case sensitive if double-quoted. Just like identifiers are handled in SQL statements.
The schema name defaults to 'public'
in my example. Adapt to your needs. You could even ignore the schema completely and default to the current search_path
.
to_regclass()
is new in Postgres 9.4. For older versions substitute:
IF EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = _schema
AND table_name = _tbl
) THEN ...
This is actually more accurate, because it tests exactly what you need. More options and detailed explanation:
Always defend against SQL injection when working with dynamic SQL! The cast to regclass
does the trick here. More details:
DO
cannot return anything. You have to use a stored procedure for that (with languageplpgsql
-- for exception handling). -- also no rows returned does not equal to a single row returned, with an empty array in it (i.e. in js, this means[] != [{col1:[]}]
). – Brunelleschiunnest
to my example to produce the desired behavior (don't know how to do this more elegantly). Anyway, do I have to declare procedure each time I am executing a query with exception handling? Is there no other option? – Alkalinevalue
? Or a set of rows? And is this for one hard-coded table name or for a variety of possible table names? – Servomechanismplpgsql
's exception handling is mainly for stored "logic". But what type of exception do you afraid of? Maybe, there is an alternative. – BrunelleschiSELECT ('myschema','mytable') IN (SELECT table_schema,table_name FROM information_schema.tables);
to check whether the table exists, and if it does, I'm selecting its rows. But it often happens that the table ceases to exist before the second query is executed (i.e., the idiom "if table exists, select its contents" does not work). This is why I wish to simply return empty result if an exception occurs. – Alkaline