Re: Assert single row returning SQL-standard functions

From: Pavel Stehule <pavel(dot)stehule(at)gmail(dot)com>
To: Joel Jacobson <joel(at)compiler(dot)org>
Cc: Vik Fearing <vik(at)postgresfriends(dot)org>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Assert single row returning SQL-standard functions
Date: 2025-08-29 16:14:17
Message-ID: CAFj8pRBL0hEucjs9-vEnszBOo=3pdCtKBtmo_5Mxb+8zu4QNqQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi

pá 29. 8. 2025 v 16:38 odesílatel Joel Jacobson <joel(at)compiler(dot)org> napsal:

> On Fri, Aug 29, 2025, at 16:09, Vik Fearing wrote:
> > The implementation is *supposed* to track several things for a query. I
> > am not sure PostgreSQL does this accurately or not.
> >
> >
> > The information is available through the GET DIAGNOSTICS command which
> > postgres does not support (yet?).
> >
> >
> > So I might suggest something like:
> >
> >
> > SELECT a
> > FROM foo
> > WHERE b = $1
> > CHECK DIAGNOSTICS (ROW_COUNT = 1)
>
>
> >
> > and
> >
> >
> > UPDATE foo
> > SET a = $1
> > WHERE b = $2
> > CHECK DIAGNOSTICS (ROW_COUNT = 1)
> >
> > etc.
> >
> >
> > CHECK is already a reserved word in both postgres and the standard.
>
> +1
>
> I think that would be very readable. The meaning of the syntax
> should be obvious to someone who knows what a CHECK constraint is, and
> the parenthesis make it extendable.
>
> I note CHECK is also a reserved word in DB2, MySQL, Oracle, SQL Server
> and Teradata.
>
> In the meantime, while waiting for SQL-standardization,
> I wonder if we can do better than the below as a work-around?
>
> CREATE TABLE footab (id INT);
> INSERT INTO footab (id) VALUES (1), (10), (10);
>
> CREATE OR REPLACE FUNCTION assert_not_null(val anyelement)
> RETURNS anyelement
> LANGUAGE plpgsql
> IMMUTABLE
> AS $$
> BEGIN
> IF val IS NULL THEN
> RAISE EXCEPTION 'unexpected null or zero rows';
> END IF;
> RETURN val;
> END;
> $$;
>

> CREATE OR REPLACE FUNCTION _test_update(_a int)
> RETURNS SETOF int
> BEGIN ATOMIC
> UPDATE footab SET id = _a WHERE id = _a RETURNING id;
> END;
>
> CREATE OR REPLACE FUNCTION test_update(_a int)
> RETURNS int
> RETURN (SELECT assert_not_null((SELECT _test_update(_a))));
>
> joel=# SELECT test_update(100);
> ERROR: unexpected null or zero rows
> CONTEXT: PL/pgSQL function assert_not_null(anyelement) line 4 at RAISE
> SQL function "test_update" statement 1
>
>
Probably there is no other solution

CREATE OR REPLACE FUNCTION check_count(bigint)
RETURNS int AS $$
BEGIN
IF $1 <> 1 THEN
RAISE EXCEPTION 'unexpected number of rows';
END IF;
RETURN $1;
END;
$$ LANGUAGE plpgsql;

(2025-08-29 18:07:28) postgres=# select check_count((select count(*) from
pg_class where oid = 'pg_class'::regclass));
┌─────────────┐
│ check_count │
╞═════════════╡
│ 1 │
└─────────────┘
(1 row)

But all is +/- variant of your design

How useful is checking row_count other than one?

I am not too serious now, I am just playing (and I remember this discussion
many times). We can "theoretically" introduce new keyword `EXACT`, that can
specify so any DML or SELECT can process or returns just one row (or with
other clause zero rows)

EXACT ONE SELECT id FROM tab WHERE id = 1;
EXACT ONE UPDATE ...
EXACT ONE DELETE ...
EXACT ONE OR NONE SELECT ...

/Joel
>
>
>

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Pavel Stehule 2025-08-29 16:17:13 Re: Assert single row returning SQL-standard functions
Previous Message Nathan Bossart 2025-08-29 16:02:02 Re: Improve LWLock tranche name visibility across backends