update sqlc upsert article
This commit is contained in:
parent
9ac2a7ae9a
commit
352a106ceb
1 changed files with 7 additions and 30 deletions
|
|
@ -52,12 +52,8 @@ And create a `SaveUser` query for saving user records. When a conflict happens o
|
|||
|
||||
```sql
|
||||
-- name: SaveUser :one
|
||||
INSERT INTO users (
|
||||
id, name, email
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
) ON CONFLICT (id) DO UPDATE
|
||||
SET name = $2, email = $3
|
||||
INSERT INTO users (id, name, email) VALUES (@id, @name, @email) ON CONFLICT (id) DO UPDATE
|
||||
SET name = @name, email = @email
|
||||
RETURNING *;
|
||||
```
|
||||
|
||||
|
|
@ -76,35 +72,16 @@ As you can see, the ID field is a `uuid.UUID`, not `*uuid.UUID`, so it's not pos
|
|||
|
||||
## The solution
|
||||
|
||||
So how do we perform upserts that account for the zero-value of our types? I've opted to create a function that provides default values when our type's zero-value is encountered. While the below function is explicitly written for `uuid` zero values, it can just as easily be ported to other types.
|
||||
|
||||
```sql
|
||||
CREATE OR REPLACE FUNCTION public.uuid_if_empty(id uuid) RETURNS uuid
|
||||
LANGUAGE plpgsql
|
||||
AS $$BEGIN
|
||||
IF id = uuid_nil() THEN
|
||||
RETURN uuid_generate_v4();
|
||||
ELSE
|
||||
RETURN id;
|
||||
END IF;
|
||||
END$$;
|
||||
```
|
||||
So how do we perform upserts that account for the zero-value of our types? I've opted to combine `nullif` and `coalesce` to achieve the upsert effect. While the solution below specifically implements upserts for `UUID` types, the `nullif` call can be adapted to any type's zero value.
|
||||
|
||||
> Thanks to [mariusor](https://lobste.rs/~mariusor) on lobste.rs for notifying me about `uuid_nil()`
|
||||
|
||||
Now in our upsert statement, we use our new function instead of the raw value passed in
|
||||
|
||||
```sql
|
||||
-- name: SaveUser :one
|
||||
INSERT INTO users (
|
||||
id, name, email
|
||||
) VALUES (
|
||||
-- Note how naming the `id` param changes the numeric value of positional args.
|
||||
-- It may be preferable to use the sqlc.arg macro here to improve readability.
|
||||
uuid_if_empty(sqlc.arg(id))::uuid, $1, $2
|
||||
) ON CONFLICT (id) DO UPDATE
|
||||
SET name = $2, email = $3
|
||||
INSERT INTO users (id, name, email) VALUES (coalesce(nullif(@id, uuid_nil()), uuid_generate_v4()), @name, @email) ON CONFLICT (id) DO UPDATE
|
||||
SET name = @name, email = @email
|
||||
RETURNING *;
|
||||
```
|
||||
|
||||
This statement coalesces to `uuid_generate_v4()` when `@id` has the UUID zero value, and uses `@id` as-is otherwise. Using `@id` when it is not the type's zero value is what allows the `ON CONFLICT` portion of the query to activate when we're updating existing records.
|
||||
|
||||
This turned out to be a very simple solution for my current problem. If there's a better or more idiomatic way to achieve the same result, please let me know in the comments!
|
||||
|
|
|
|||
Loading…
Reference in a new issue