I'm trying to write a query for Postgres to insert a row into one table which as a side-effect may insert a row into a second table (due to the first table having a foreign key constraint on the second).
I suppose I could do this as two separate queries, possibly inside a transaction, but now that I've started down the path of a single query, I'd like to know how it might be done, if it's possible at all.
I've been trying to use the general structure laid out in How to use RETURNING with ON CONFLICT in PostgreSQL? and Insert if names do not exist and return ids if exist, which seemed like a reasonable starting point, but I'm getting the error WITH clause containing a data-modifying statement must be at the top level
, and I'm not sure how to follow that advice.
Table a
is something like this:
CREATE TABLE a (
a_uuid uuid PRIMARY KEY,
b_uuid uuid REFERENCES b(b_uuid)
)
and table b
is:
CREATE TABLE b (
b_uuid uuid PRIMARY KEY,
b_name text UNIQUE
)
I want to insert a row into a
where b_uuid
is computed based on input that'll match b_name
. If b
already has the row, then use the corresponding b_uuid
; otherwise, create a new row in b
with that text and a newly-generated UUID, returning the latter to use as part of the insert into a
. This was what I had written before I realized I was in over my head:
WITH new_b AS (
WITH input (b_uuid, b_name) AS (
VALUES (
gen_random_uuid(), $1
)
), ins AS (
INSERT INTO b (
b_uuid, b_name
)
TABLE input
ON CONFLICT DO NOTHING
RETURNING b_uuid
)
TABLE ins
UNION ALL
SELECT b_uuid FROM input
)
INSERT INTO a (a_uuid, b_uuid)
VALUES (
gen_random_uuid(), (SELECT b_uuid FROM new_b)
)
Am I anywhere close? What would be the best way of doing this?