PostgreSQL Params PostgreSQL · Named Blocks · Params

PL/pgSQL Parameter Modes

Master function parameter modes — IN (default), OUT (return values), and INOUT (both directions). Critical fixes: original WHERE clause referenced undefined film_title variable, and INOUT example used PERFORM which discards the modified value. Use SELECT INTO not PERFORM to capture INOUT outputs in a PL/pgSQL block.

PL/pgSQL functions support three parameter modes: IN, OUT, and INOUT. They control whether the function reads a parameter, writes to it, or both:

ModeDirectionDefault value supplied by
IN (default)Caller → functionCaller; ignored after the call returns
OUTFunction → callerFunction assigns; caller can't initialize
INOUTBoth directionsCaller initializes; function can modify

Most parameters are IN — that's the default if you don't specify a mode. OUT and INOUT exist for the special case where you want a function to return multiple values without bundling them into a composite type.

📌 PROCEDURES vs FUNCTIONS for parameter modes. Procedures support only IN and INOUT — not OUT. Functions support all three. If you're writing a procedure, use INOUT for outputs (covered on the procedure pages).

An IN parameter is data flowing into the function. Changes inside the function don't escape — the caller's value is unaffected. Most parameters fit this pattern.

Example 1 — IN Parameter
PL/pgSQL — IN parameter
CREATE OR REPLACE FUNCTION add_numbers(a INT, b INT)  -- IN is implicit
RETURNS INT
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN a + b;
END;
$$;

SELECT add_numbers(5, 3);
OUTPUT
psql — IN params
postgres=#SELECT add_numbers(5, 3);
add_numbers
-------------
8
(1 row)

An OUT parameter is data flowing out of the function — a way to "return" multiple named values without using a composite type. The function assigns to the OUT parameter; the caller receives it as part of the function's result.

When a function has OUT parameters, you don't write a RETURNS clause for the same values — the OUT parameters' types implicitly define the return shape.

Example 2 — Single OUT Parameter

Look up a film's length by title. Setup:

PL/pgSQL — Setup
DROP TABLE IF EXISTS film;

CREATE TABLE film (
    film_id SERIAL PRIMARY KEY,
    title   VARCHAR(255) NOT NULL,
    length  INT
);

INSERT INTO film (title, length) VALUES
    ('Film A',  90),
    ('Film B', 120),
    ('Film C', 150),
    ('Film D',  80),
    ('Film E',  30),
    ('Film F',  60);
PL/pgSQL — get_film_length with RETURNS
CREATE OR REPLACE FUNCTION get_film_length(p_title VARCHAR)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE
    film_length INT;
BEGIN
    SELECT f.length INTO film_length
    FROM   film f
    WHERE  f.title = p_title;

    RETURN film_length;
END;
$$;

SELECT get_film_length('Film A');
OUTPUT
psql — get length
postgres=#SELECT get_film_length('Film A');
get_film_length
-----------------
90
(1 row)
⚠️ The original tutorial's function had a critical bug — the function declared parameter title VARCHAR, then in the WHERE clause referenced film_title (which doesn't exist anywhere). The function would fail to create with "column film_title does not exist". Fixed: parameter renamed to p_title (avoiding shadowing the column name) and used in the WHERE clause.

Naming tip: prefix parameters with p_ when they might conflict with column names. WHERE title = title is ambiguous — does that mean column = parameter, or column = column? Best to avoid the question entirely.

Example 3 — Multiple OUT Parameters

OUT parameters earn their keep when you want to return multiple named values:

PL/pgSQL — multiple OUT parameters
CREATE OR REPLACE FUNCTION get_film_details(
    p_film_id INT,
    OUT title  VARCHAR,
    OUT length INT
)
LANGUAGE plpgsql
AS $$
BEGIN
    SELECT f.title, f.length
    INTO   title, length
    FROM   film f
    WHERE  f.film_id = p_film_id;
END;
$$;

SELECT * FROM get_film_details(1);
OUTPUT
psql — film details
postgres=#SELECT * FROM get_film_details(1);
title | length
--------+--------
Film A | 90
(1 row)

Three things happening here:

  • No RETURNS clause — the OUT parameters define the shape of the result.
  • No RETURN statement in the body — assignments to the OUT params are the return value.
  • Called as SELECT * FROM — treats the function call as a row source so the columns spread.

An INOUT parameter starts with a value supplied by the caller and ends with a value supplied by the function. Effectively a parameter that's both read and written.

Example 4 — INOUT Parameter

A function that adjusts a length by a given amount — passes len in, modifies it, returns it:

PL/pgSQL — adjust_length
CREATE OR REPLACE FUNCTION adjust_length(
    INOUT len    INT,
          adjustment INT
)
LANGUAGE plpgsql
AS $$
BEGIN
    len := len + adjustment;
END;
$$;

-- From SQL: returns the modified value
SELECT adjust_length(100, 20);

-- From a PL/pgSQL block: the variable is updated in place
DO $$
DECLARE
    new_length INT := 100;
BEGIN
    SELECT adjust_length(new_length, 20) INTO new_length;
    RAISE NOTICE 'New length: %', new_length;
END
$$;
OUTPUT
psql — adjust length
postgres=#SELECT adjust_length(100, 20);
adjust_length
---------------
120
(1 row)
postgres=#DO $$ DECLARE new_length INT := 100; BEGIN SELECT adjust_length(new_length, 20) INTO new_length; RAISE NOTICE 'New length: %', new_length; END $$;
NOTICE: New length: 120
DO
⚠️ The original tutorial used PERFORM adjust_length(new_length, 20) to call the function from a DO block. That's wrong: PERFORM discards the function's return value, so the modified new_length would never be captured. To get the value back into the caller's variable, you must use SELECT function_call(...) INTO variable. Fixed in the example above.
Want to…Best fit
Pass data into a functionIN (the default)
Return a single computed valuePlain function with RETURNS type + RETURN
Return multiple named valuesMultiple OUT parameters
Pass in, modify, and return one parameterINOUT
Return many rowsRETURNS TABLE(...) or RETURNS SETOF (next pages)
Return a row from a table typeRETURNS table_name with a %ROWTYPE variable
💡 If you find yourself reaching for OUT parameters for many values, consider RETURNS TABLE(...) instead. A function with three OUT parameters and one returning a single-row TABLE are functionally similar; the TABLE form is cleaner when callers will always use it as a row source.
  1. Default to IN parameters. Most data flows in, not out. Reach for OUT/INOUT only when you need to.
  2. Prefix parameters to avoid column-name collisions. p_title beats title when the table has a title column. Saves you from WHERE title = title ambiguity.
  3. Use SELECT ... INTO to capture INOUT results in a PL/pgSQL block, not PERFORM. PERFORM discards return values.
  4. Don't mix RETURNS with OUT parameters — pick one. Either an explicit return type with RETURN in the body, or OUT parameters that define the shape implicitly.
  5. For multi-row outputs, use RETURNS TABLE or SETOF rather than collecting into OUT arrays. Cleaner and more idiomatic.
  6. Document the parameter direction in the function name or comment. increment_counter(INOUT cnt) reads more clearly than just increment_counter(cnt).
  • Functions support three parameter modes: IN (default, caller-to-function), OUT (function-to-caller), INOUT (both).
  • Most parameters are IN — pass data into the function. Changes don't escape.
  • OUT parameters let a function return multiple named values without a composite type. No RETURNS clause needed when using OUT.
  • INOUT is rare but useful when a function should mutate a passed value in place.
  • To capture INOUT/OUT results in a PL/pgSQL block, use SELECT function(...) INTO var, not PERFORM.
  • For many rows, prefer RETURNS TABLE or RETURNS SETOF over arrays of OUT parameters.