Summary: in this tutorial, you’ll learn how to handle exceptions using the PL/pgSQL EXCEPTION
block.
Introduction to the PL/pgSQL EXCEPTION Block #
In PL/pgSQL, an exception is an unexpected condition or error during execution. Exceptions may result from issues like constraint violation, division by zero, or data not found.
To handle exceptions, you use the EXCEPTION
block within the BEGIN
and END
keywords of a block:
DO
$$
DECLARE
-- declaration
BEGIN
-- code that may cause exceptions
EXCEPTION
WHEN exception_name THEN
-- Handle the specific exception
RAISE NOTICE 'An error occurred: %', SQLERRM;
WHEN OTHERS THEN
-- Handle all other exceptions
RAISE NOTICE 'An unknown error occurred: %', SQLERRM;
END;
$$
LANGUAGE plpgsql;
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
In this syntax:
- The
WHEN
clause catches specific exceptions such as division by zero or unique constraint violation. - The
WHEN OTHERS
clause catches all exceptions you have not explicitly handled.
The SQLERRM
and SQLSTATE
are error message variables:
- The
SQLERRM
variable contains the exception’s message. - The
SQLSTATE
variable holds the error code.
For a comprehensive list of exception names and SQL error codes, check out this SQL Error Codes page.
Handling a Division by Zero Exception Example #
The following statement create a function called try_divide
that returns the division of two operands:
CREATE FUNCTION try_divide(x NUMERIC, y NUMERIC)
RETURNS NUMERIC AS
$$
BEGIN
RETURN x / y;
EXCEPTION
WHEN division_by_zero THEN
RAISE NOTICE 'Cannot divide by zero, returning NULL!';
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
How the function works:
- Divide
x
byy
and return the result. - If
y
is zero, adivision_by_zero
exception will occur. TheEXCEPTION
block handles it by raising a notice and returningNULL
.
For example, the following statement calls the try_divide
to divide 10 by 2:
SELECT try_divide(10, 2);
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Output:
try_divide
--------------------
5.0000000000000000
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
However, when you divide 10 by zero, it will return NULL
and issue a notice:
SELECT try_divide(10, 0);
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Output:
NOTICE: Cannot divide by zero, returning NULL!
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Handling a NO_DATA_FOUND Exception Example #
The NO_DATA_FOUND
exception occurs when a SELECT INTO
statement does not retrieve any row.
The following example creates a function called get_inventory_quantity
that get an inventory quantity of a product specified by a product id:
CREATE FUNCTION get_inventory_quantity (id INT)
RETURNS INT AS
$$
DECLARE
v_qty INT;
BEGIN
SELECT quantity INTO STRICT v_qty
FROM inventories
WHERE product_id = id;
RETURN v_qty;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NOTICE 'No product found with the id %', id;
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
How the function works:
First, retrieve the quantity of the product specified by id
and assign it to the v_qty
variable using the SELECT INTO
statement with the STRICT
option:
SELECT quantity
INTO STRICT v_qty
FROM inventories
WHERE product_id = id;
Second, return the quantity:
RETURN v_qty;
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Third, if the product with the id
does not exist, the NO_DATA_FOUND
exception occurs. In this case, we raise a notice and return NULL
in the EXCEPTION
block:
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NOTICE 'No product found with the id %', id;
RETURN NULL;
Code language: PHP (php)
The following statement calls the get_inventory_quantity
function to get the inventory quantity of the product with id 9999
:
SELECT get_inventory_quantity (9999);
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Output:
NOTICE: No product found with the id 9999
get_inventory_quantity
------------------------
NULL
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Handling a TOO_MANY_ROWS Exception #
The following creates a function that finds a product by safety stock:
CREATE FUNCTION find_product_by_safety_stock (qty INT)
RETURNS products
AS
$$
DECLARE
v_product products%ROWTYPE;
BEGIN
SELECT *
FROM products
INTO STRICT v_product
WHERE safety_stock = qty;
RETURN v_product;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NOTICE 'No product found with the safety stock qty %', qty;
RETURN NULL;
WHEN TOO_MANY_ROWS THEN
RAISE NOTICE 'More than one product found with the safety stock qty %. Getting the one with the highest price', qty;
SELECT *
FROM products
INTO STRICT v_product
WHERE safety_stock = qty
ORDER BY price DESC
LIMIT 1;
RETURN v_product;
END;
$$
LANGUAGE plpgsql;
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
How the function works:
The function finds the product with the specific safety stock from the products
table. If there is more than one product, the EXCEPTION
block raises a notice and returns the product with the highest price.
The following statement calls the find_product_by_safety_stock
function to find the product with a safety stock of 10:
SELECT
product_name,
safety_stock,
price
FROM
find_product_by_safety_stock (10);
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Output:
NOTICE: More than a product found with the safety stock qty 10
product_name | safety_stock | price
------------------+--------------+--------
Sony Xperia 1 VI | 10 | 949.99
Code language: PostgreSQL SQL dialect and PL/pgSQL (pgsql)
Summary #
- Use the
EXCEPTION
block to handle exceptions that occur during the execution of your PL/pgSQL code.