-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Allow strings in field/2
#4384
Allow strings in field/2
#4384
Conversation
Interesting. What happens if there is no schema? |
Ah good catch, I knew I forgot something. Then it fails but it can be fixed by changing the guard in the adapter here: https://github.com/elixir-ecto/ecto_sql/blob/master/lib/ecto/adapters/postgres/connection.ex#L835 and here https://github.com/elixir-ecto/ecto_sql/blob/master/lib/ecto/adapters/postgres/connection.ex#L841 |
I think if we want to go down this route, we should rather go in the other direction. Have field_source return strings and then send it all the way down to the adapter as strings. If there is no field source, we normalize it to strings on the spot? As long as it is a small change on the adapter side, we should be good. :) |
Although converting to strings on adapters such as |
It makes the inspection a bit weird too if it's normalized to string. I had to make a special condition to make the inspect look normal. I just pushed a change |
Thanks @greg-rychlewski. I am honestly a bit unsure. It feels we are at the worst of both words: sometimes they are atom, sometimes they are strings. What do you think about:
Perhaps we can start with step 2 for now (and raise if a string is given to a schema source)? |
That sounds like a good compromise. I'll update the PR. There was one thing I was having trouble figuring out for query = from p in Post, select: field(p, :visit)
IO.inspect query
# "from p0 in Inspect.Post, select: p0.visit" But strings produce this query = from p in Post, select: field(p, "visit")
IO.inspect query
# "from p0 in Inspect.Post, select: p0 . \"visit\"()" I was previously handling it by transforming it to Do you know if there is anything I can do to make the string play nice with the dot notation? |
My only suggestion is to indeed convert it back to |
I had one idea. Would you be against tagging the The reason I'm suggesting this is because when I tried normalizing schemaless sources to string it started to affect a lot of things I didn't expect like subqueries and joins. Doing it this way would minimize the blast radius and still have some kind of internal consistency. And either way we are asking adapters to change something. |
To be clear, you are saying this:
But we still need to decide what to do with strings and schemas. We have to options:
|
I'm starting to feel like I completely misunderstood one of your earlier replies. When you said this
Were you talking about |
At the time I did not mean |
afa4369
to
4876779
Compare
Let me know what you think of this:
Line 698 in 4876779
Line 2327 in 4876779
|
we could also make it so that |
My thinking would be that, if you pass a string, then it is kept as is and it would always be the field source. We never transform or manipulate it in any case. |
Do you think it would still be ok to check its type against the schema? For instance, if someone wanted to do this field = get_user_input_as_string(...)
query = from p in Post, where field(p, ^field) == ^"some_string" To work properly the parameter would have to be cast to the type of Or alternatively they could wrap |
It really depends which problem we want to solve. Do we want to make it easier for people receiving parameters to create custom queries or do we want to make it possible for you to dynamically query databases without generating tons of atoms? |
I would say this is the bigger issue right now:
In which case I think you are saying this is not an issue currently for schemas. Because if you are using schemas then you know up front which strings are allowed and can handle it without this change? If my above statement is not mistaken, then I think even though it's a bigger problem, it might be a bit confusing why we are not solving this problem at the same time if it's not too hard to do so:
|
I think I get it though. Basically it's about staying consistent using atoms when referring to schema fields right? If so it's ok to me. I could change the PR to raise if string is used for schemas. |
The problem I think is that we have two acceptable semantics. One is to make it a direct field source access and the other is to make it automatically cast. When we pick one, we exclude the other. For schemaless fields they are the same, however. |
Yes, you could do String.to_existing_atom, althogh we can argue that we could have a more convenient API. The question is if it should be |
For example, just for brainstorming purposes, it could be called |
One reason I can think to keep it as If we go down the route of making a new function for each one it might be cumbersome for us to maintain and for users to use. So it might be worthwhile to lax the definition to include stings in the existing functions and make them have the same semantics as atoms. |
I think we can go ahead with this one? Perhaps we start by simply allowing strings on |
I think this makes sense, the columns received from the database is strings as well. |
4876779
to
fbac40f
Compare
I forgot a lot about this but please let me know if my understanding below is correct. When someone uses a string in
If ^ is correct then this PR is good to review. I will just need to update the docs for field/2 to especially callout (3) above. It will also require this ecto_sql PR to work: https://github.com/elixir-ecto/ecto_sql/pull/596/files |
Co-authored-by: felipe stival <[email protected]>
It feels kind of weird to lose all the type behaviour and other things when querying a schema. Maybe raising if used with schema is better? |
Why would we lose type behaviour, would it be to expensive to convert the atoms to strings, or even keep them around, a-kind to how Ecto Enum works? |
My understanding is the debate is between these two choices:
I understand Jose's last response to mean he's in favour of (1). So if you keep the type behaviour but not other things then the semantics are not consistent. |
Agreed. I prefer 1. We should not try to guess the user intent and this way they also behave consistently. For example, you can imagine that a string field could be used to access a source (or field_source) of a schema directly. |
@josevalim That makes sense to me. I have updated the docs so this PR and the Ecto SQL one are both ready for review whenever you have the time. As a side discussion: I think the use case getting the biggest benefit from this change is when you are dynamically managing a lot of schemaless queries at run time. i.e. there's not a tonne of structure to your queries that you know up front. There is probably another use case, which is you have a lot of structure up front but the query is being dictated by outside input. It would probably be good to have a way for users to intentionally try to use an outside string as a schema field. If you also think the above is a valid use case, would you be open to a follow-up PR or would you rather see how people use the change in this PR and what kind of feedback they give before going down that route? |
I would say that those later use cases are not blocked by this PR, right? They can use String.to_existing_atom and the existing reflection API to convert fields from strings to atoms. Perhaps we can add functionality to make their lives easier? Make a function like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful 😍
I'm worried I'm missing something obvious. But I think with these 2 simple changes to the schema functions we can allow string names in
field/2
.The
__schema__(:type, field)__
functions will now catch string names and get the right type. And the__schema__(:field_source, field)__
functions will ensure the field is converted back into an atom during normalization so that the adapters don't have to change.