I have following SQL (PostgreSQL) query:
DO $$ BEGIN
IF EXISTS (SELECT * FROM segment WHERE slug = $1 AND is_active = true) THEN
RAISE EXCEPTION $2;
ELSE
INSERT INTO segment (slug) VALUES ($1)
ON CONFLICT(slug) DO
UPDATE SET is_active = true;
END IF;
END $$;
If you wondering why I wrapped IF-ELSE statement inside $$ function: see this question.
Now I trying to use it with arguments from Golang using database/sql
and github.com/lib/pq
:
_, err := p.Exec(`
DO $$ BEGIN
IF EXISTS (SELECT * FROM segment WHERE slug = $1 AND is_active = true) THEN
RAISE EXCEPTION $2;
ELSE
INSERT INTO segment (slug) VALUES ($1)
ON CONFLICT(slug) DO
UPDATE SET is_active = true;
ND IF;
END $$;`,
slug,
exceptionMsg,
)
But when I run this then I got error with following message:
pq: got 2 parameters but the statement requires 0
Looks like sql-driver doesn’t recognize argument placeholders inside of $$ function.
Firstly I tried use query without $$ function, like so:
IF EXISTS (SELECT * FROM segment WHERE slug = $1 AND is_active = true) THEN
RAISE EXCEPTION $2;
ELSE
INSERT INTO segment (slug) VALUES ($1)
ON CONFLICT(slug) DO
UPDATE SET is_active = true;
END IF;
But I got same error as in metioned question:
pq: syntax error at or near "IF"
UPD.
I understand that it will be more simple to split this logic into two separate queries and handle it from golang code, but I want to optimize it and use only one query.
3
Answers
Try modifying your code by bringing the positional placeholders outside the dollar quoted block, so you can pass the parameters to your query without having conflicts, something like this;
As already pointed out by others, the
DO
code block doesn’t accept parameters.So you should either use a pre-declared function or, alternatively, you can use
WITH
(or CTE) to achieve pretty much the same thing.And you can use
RowsAffected
to determine if the query was successful (i.e., an insert, or an on-conflict-update occurred).I would solve the problem with a trigger:
Then all you have to run is the
INSERT ... ON CONFLICT
.