Re: Assert single row returning SQL-standard functions - Mailing list pgsql-hackers

From Pavel Stehule
Subject Re: Assert single row returning SQL-standard functions
Date
Msg-id CAFj8pRCaz68cK5kd-O5JLUKCGnOJv==GrEnyUYr9EhbGS81STA@mail.gmail.com
Whole thread Raw
In response to Assert single row returning SQL-standard functions  ("Joel Jacobson" <joel@compiler.org>)
Responses Re: Assert single row returning SQL-standard functions
List pgsql-hackers
Hi

pá 29. 8. 2025 v 9:03 odesílatel Joel Jacobson <joel@compiler.org> napsal:
Dear fellow hackers,

Background:

Commit e717a9a "SQL-standard function body" introduced support for
SQL-standard functions, which have two great benefits compared to
plpgsql functions:

*) Dependency tracking

*) Renaming of database objects seamless, thanks to function body being
parsed at function definition time and stored as expression nodes.

Problem:

I really wish I could use such functions more often, but a very common
pattern in my database functions is the need to ensure exactly one row
was returned by a statement, which is currently only achievable via
plpgsql and its INTO STRICT.

I think we just need a way to assert return of a single row per
function, since if needed per statement, we can could just create
separate SQL-functions for each such statement, and execute them
separately, from a single function, if multiple statements are needed
within a single function.

Ideas on possible solutions:

How about piggy-backing on the CREATE FUNCTION's existing ROWS
parameter, and reusing the [ ENFORCED | NOT ENFORCED ] terminology we
have for constraints? We would need to lift the current restriction that
it is only allowed when the function return a set.

CREATE FUNCTION test_update(_a int, _b int)
RETURNS BOOLEAN
ROWS 1 ENFORCED
BEGIN ATOMIC
    UPDATE foo SET a = _a WHERE b = _b RETURNING TRUE;
END;

CREATE FUNCTION test_select(_b int)
RETURNS INT
ROWS 1 ENFORCED
BEGIN ATOMIC
    SELECT a FROM foo WHERE b = _b;
END;

Alternatively, maybe we could set a per-function GUC,
e.g. SET assert_single_row = true?

CREATE FUNCTION test_update(_a int, _b int)
RETURNS BOOLEAN
SET assert_single_row = true
BEGIN ATOMIC
    UPDATE foo SET a = _a WHERE b = _b RETURNING TRUE;
END;

CREATE FUNCTION test_update(_a int, _b int)
RETURNS BOOLEAN
SET assert_single_row = true
BEGIN ATOMIC
    SELECT a FROM foo WHERE b = _b;
END;

INSERT and DELETE should be supported as well of course.

These were just the two first ideas on the top of my head, please share
yours if you see a better way.

To me, if we can solve this problem, it would mean a huge improvement in
how I work with database functions in PostgreSQL, since I would then get
the nice benefits of dependency tracking and a more declarative mapping
of how all database objects are connected to functions.

I hope we can solve it together somehow.

It is a question if there is some benefit or necessity to allow NON STRICT behaviour there, and maybe it can be better to generally check if the result is not trimmed?

Secondary question is a fact, so proposed behaviour effectively breaks inlining (what can be a performance problem, although for 18+ less than before).

The requested behaviour can be forced by using subquery and RETURN command - and if I remember some articles and books related to this topic, then subselects was used instead INTO 

SET var = (SELECT col FROM tab WHERE id = x);

Instead SELECT col INTO var FROM tab WHERE id = x;

(2025-08-29 09:19:24) postgres=# CREATE OR REPLACE FUNCTION fx(_a int) RETURNS bool RETURN (SELECT id = _a FROM footab WHERE id = _a);
CREATE FUNCTION
(2025-08-29 09:19:31) postgres=# SELECT fx(1);
┌────┐
│ fx │
╞════╡
│ t  │
└────┘
(1 row)

(2025-08-29 09:19:33) postgres=# SELECT fx(10);
ERROR:  more than one row returned by a subquery used as an expression
CONTEXT:  SQL function "fx" statement 1


Subquery cannot be used when there are more than one OUT argument

Regards

Pavel

 

/Joel


pgsql-hackers by date:

Previous
From: Yugo Nagata
Date:
Subject: pgbench: extend variable usage in scripts
Next
From: Pavel Stehule
Date:
Subject: Re: pgbench: extend variable usage in scripts