Top-Level quat passing to query expansions #2420
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
In #2381 quill's top-level query expansion using
materializeQueryMeta
was removed in favor of the existing functionality inExpandNestedQueries
expanding identifiers from their quat information.That is to say, instead of doing this:
Instead we get the information by expanding the quat like this:
There are some situations however where the Quat inside the EntityQuery inferred as generic. For example in a query that looks like this:
It will get parsed to something like this:
Since when the Infix is created the quat type
Q
is still generic, the Quat in theInfix
element will be inferred asGeneric
. This means that we do not know to expand an identifier downstream of this index into it's corresponding components:This incomplete knowledge resulted in #2403 and would cause other similar issues if left unfixed.
First Solution: Transparent/Generic Infixes
One possible solution is to have a notion of a Transparent infix i.e. infix where the Quat of the Ast inside the
params
element is treated as the "Real" Quat instead of what is inside the infix. Originally in #2403 I thought to reduce all the Quats of all the params together using theQuat.leastUpperType
function meaning that if as in the above case, there was a singleparams
element (i.e. ourEntityQuery("Person")
it's Quat would be the one that would be swapped into the surrounding infix. This was to be done both in the parser as well as the RepropagateQuats transformation phase via theQuat.improveInfixQuat
method.Unfortunately this solution quite a few edge-cases in which it does not property function. For example, say that the infix has multiple params whose types have nothing to do with one another:
The infix element will look like this:
When doing a
Quat.leastUpperType
acrossQuat.Product("name"->Quat.Value, "age"->Quat.Value)
andIdent("i", Quat.Value)
you can at best say that it is aQuat.Generic
orQuat.Unknown
which does not help expand thep.?
value in the Select at all. The only place where transparent Infixes actually make sense is in the case where there is only one param. This may be useful to getting better quat-typing in Spark queries in the future (e.g. in #2406) but beyond that it has little value. Something else needs to replace thematerializeQueryMeta
functionality for these kinds of infixes.Better Solution: Top Level Quats
The fundamental thing that
materializeQueryMeta
could was was that for somerun(Query[T])
it would compute a.map(t => t.fieldA, t.fieldB, ...)
. ThisT
is injected at the last moment of the query composition hence any abstract/generic types used in the query are changed to the concrete value ofT
(or something related to it). This has to be the case becauseT
can only be a CaseClass, Tuple, or decodeable value, otherwise Quill will fail to compile theQuery[T]
.That means that if we modify the
run
function (or the quotation right before) to produce a Quat fromT
, the this "top-level" quat will always represent the final concrete type to which the Query needs to be decoded. This logic in ExpandNestedQueries (particularly in the SelectPropertyProtractor) will look roughly as follows:That is to say, in the SelectPropertyProtractor if it is detected that an identifier has a generic quat and that identifier is at the top-level of an SQL query, use the topLevelQuat (
QV
orQuat.Product("name"->Quat.Value, "age"->Quat.Value)
in our case above).Thus since materializeQueryMeta expanded our Query with information only known at the top-level, previous functionality that expanded queries based on Quats had unhandled edge cases. Since the top-level-quat knows everything that materializeQueryMeta knew, they should be a complete replacement.