-
Notifications
You must be signed in to change notification settings - Fork 804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sqlc.embed() generates code that cannot handle NULL items #2348
Comments
Playground reproduction: https://play.sqlc.dev/p/f7baf03ee43839b8063555ea33f6d94c2f9f8545e14cb7f41406f880eea432d7 I may actually try to fix this, as it also affects my planned use for |
Hi Both, Left joins were left out of the implementation because the scanning logic is not so straightforward. Passing the additional context through about a left-join column is also tricky.
I think most of this idea has legs (temp type with nullable fields, nil object pointer, seem 👍 ), but there are two issues I can see.
Both of those options are solvable, perhaps with config overrides, but it's worth pointing out. This feature requires sqlc to make business logic decisions which it typically stays out of. I would love to see this implemented, just to make I hope this helps. |
Yeah, sometimes it's a question of "where do you stop?" with a feature like this I guess. 🤔 It'd be tempting to explicitly provide the support if the primary key is available, and bail out to the standard behaviour if not. This would surely increase its utility quite a bit. However, I do wonder if there's an opportunity for the generator logic to be a bit more well-informed about which joins could result in a null Alternatively, could there be room for a |
Good points nickjackson. I also think richchurcher has a point on the ability to configure the embed() command with parameters. I think a good start could be to write the code such that it is able to detect the cases it cannot support. Right now the code generated doesn't really work if you happen to do unsupported things. If it is possible to detect all the cases it doesn't work, I think it would be easier to extend it to actually make it work, since then we know all about what type of join it is and so forth. |
Yeah, I have to be honest the implementation (and the whole of sqlc to some extent) is very naive about what a join is - it is just sugar on column selections. Happy to answer questions and help out where I can, but I don't have the bandwidth to do this myself, nor do I have any authority to suggest the right course of action. (I'm not a maintainer) |
I'll have a quiet look at what it would take to go down the parameter path... it feels like it would just be a question of choosing a pointer type, but it can't possibly be that simple 😆 |
@nickjackson If you did happen to have a moment, could you glance at #2472 and see if you think a more polished version of this might work? |
I think I understand the motivation for this after reading, but based on the current PR it's going to require a lot of changes including changes to templates so I'm not sure if it's likely to get merged. Just to clarify, would you say the "workaround" for this is to abandon use of |
Oh I don't think it'll get merged either, but I'm glad to have talked about the issue more :) At the moment, this is my workaround for nullable joins with -- name: GetUserRelationships :one
select
sqlc.embed(u),
sqlc.embed(b),
i.id as img_id,
i.object_key as img_object_key,
i.created_at as img_created_at,
l.id as loc_id,
l.code as loc_code,
l.english_name as loc_english_name,
l.te_reo_name as loc_te_reo_name
from users u
left join buckets b on u.bucket_id = b.id
left join images i on u.avatar_id = i.id
left join locations l on u.location_id = l.id
where u.deleted_at is null
and u.id = $1
limit 1; Being consistent with that naming scheme lets me copy the values into structs in a relatively pain-free fashion. It's work, but it's not too much work, at least for now. |
Unfortunately even with that I still get the Scan error, so I was forced to write two queries, one with the possible null relations and other without it, but is quite painful -- name: ListChargePaginate :many
SELECT
sqlc.embed(c),
sqlc.embed(cl),
sqlc.embed(t),
sqlc.embed(e),
sqlc.embed(ctype),
sqlc.embed(cstate),
sqlc.embed(ctime),
sqlc.embed(p)
FROM charge c
LEFT JOIN client cl ON c.client_id = cl.id
LEFT JOIN transport t ON c.transport_id = t.id
LEFT JOIN employee e ON c.manager_id = e.id
LEFT JOIN charge_type ctype ON c.charge_type_id = ctype.id
LEFT JOIN charge_state cstate ON c.state_id = cstate.id
LEFT JOIN charge_time ctime ON c.type_id = ctime.id
LEFT JOIN payment_method p ON c.payment_method_id = p.id
WHERE c.deleted_at IS NULL
ORDER BY c.created_at DESC
OFFSET $1 LIMIT $2;
In this case the relations that can be null is transport and manager |
Yeah, you won't be able to use |
Workaround: if you add a view with just your CREATE VIEW authorimages AS (
SELECT images.* FROM authors LEFT JOIN images ON authors.id = images.id
);
-- name: GetAuthor :one
SELECT sqlc.embed(authors), sqlc.embed(authorimages) FROM authors
JOIN authorimages on authors.id = authorimages.id
WHERE authors.id = $1 LIMIT 1; type Authorimage struct {
ID sql.NullInt64
Url sql.NullString
} |
You don't even need to join inside that view, do you? Ie. Either way that's a clever workaround that I'm probably going to rely on for now, I just wish it didn't introduce a model with all nullable fields in our domain that only technically accommodates for only null or a model with all non-nullable fields. Dreaming of |
Thanks Man, I never thought about that, I didn’t know that a view generate Null fields. |
Hey, I'm stumped by this. Do you have a better way? Thanks. |
You can use views to handle that. see #2997 |
@KevenGoncalves |
@KevenGoncalves does workaround with views makes sense with multiple tables? This is my query:
Here is schema:
|
Update:
|
Version
1.18.0
What happened?
I was testing to implement sqlc.embed() in order to simplify my conversions routines from sqlc to protobuf.
I have this query:
The input is a list of IDs that i expect to get from the DB. If the ID is not found I expect to get a NULL row. Unfortunately it seems like the code for the new sqlc.embed() feature doesn't handle this case very well since I get a scan error if I try to query for a non-existent ID.
pgx.ScanArgError:
can't scan into dest[0]: cannot scan null into *string
The generated output code:
I'm actually not sure how to solve this. I need the LEFT JOIN because reasons...
Perhaps it's impossible to support this case?
Perhaps sqlc can create a temp/hidden type where all fields are
NULL
-able. Scan the fields using this type and then check if thePRIMARY KEY
fields areValid
. If they are valid, convert the type with theNULL
-able fields into the normal type. If the fields are not valid then return a nil object pointer for the embedded field.@nickjackson, any input?
What operating system are you using?
Linux
What database engines are you using?
PostgreSQL
What type of code are you generating?
Go
The text was updated successfully, but these errors were encountered: