Perform runtime fragment splicing in Ecto#4697
Perform runtime fragment splicing in Ecto#4697greg-rychlewski merged 1 commit intoelixir-ecto:masterfrom
Conversation
| def splice!(value, param_num) do | ||
| if is_list(value) do | ||
| value | ||
| Enum.map(value, fn _ -> {:^, [], [param_num]} end) |
There was a problem hiding this comment.
This is the weirdest part. Because we use length(params) to set the parameter indexes and we don't know how many are in here until runtime.
Doing it this way keeps parameter counting in the rest of the builder the same and planner cleans everything up anyways.
There was a problem hiding this comment.
It has been a while, so can you please remind me how it works in this case? fragment(..., splice(^foo), ^bar) How do we know the parameter position for ^bar? I guess the planner goes increment all parameters later on, whenever it sees a splice or an in?
There was a problem hiding this comment.
Yeah basically like that.
To be more specific, when normalizing a query the prewalk function in the planner has an accumulator that keeps track of the current query parameter number. The counter is initialized to 0 for queries and initialized to the current parameter count when doing stuff like insert_all or conflict queries. Whenever the prewalker sees a query parameter it just increments the parameter counter.
For :in and the current implementation of :splice they have a bit of a trick in the prewalker. Since we don't know the length of the list at compile time we treat the whole list as one query parameter then in the prewalker it shifts the counter by the length of the list. See here for :in and here for :splice. Then in the adapter it sees the AST for these things and does the actual splicing of the SQL text. The parameter list itself is spliced by the planner here.
josevalim
left a comment
There was a problem hiding this comment.
If you are happy with it, I am happy with it!
Pre-requisite to removing it from the adapters