Fixing Embedded Coproduct Column Duplication Issue #1604
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.
Fixes #1602
Introduction
The primary challenge of managing sub-queries with row co-products is how to alias columns that have the same name. For example say that we have something like this:
Running this should return something like this:
Now let's say we make a subquery of this:
Doing a subquery of this causes a problem. The inner query returns two name columns and SQL cannot determinte one from the other!
In order to solve this problem, Quill aliases the inner query with an alias which is the appended the tuple property name (i.e.
_+(tupleIndex+1)
) and the property name. I.e:This strategy works for tuples but what about if the co-product type is a case-class. This kind of scenario is more difficult to reproduce but as indicated by #1602 it is:
This yields:
Note how the "name" columns as well as others appear twice (and it also does not correspond to the inner aliast
taname
). This is because only tuple properties (e.g._1
,_2
...) are only appended to sub-query aliases as opposed to Ad-Hoc case class fields.That is to say
Property(Property(x, "_1"), "name")
is expressed as"_1name"
inSqlIdiom
butProperty(Property(x, "pa"), "name")
just becomes"name"
.This is by design since we rely on the skipping of nested property objects in order to get correct SQL tokenization behavior in embedded entities. As the next section will explain.
Sub-Properties and Embedded Entities
Typically the way embedded entities are expressed is as properties e.g. with a schema like this:
... selecting the field
a
fromEmb
inside ofParent
would bep.map(p => p.emb.a)
. This translates intoProperty(Property(p, "emb"), "a")
in the AST. Now if we were to treat both tuple index (i.e. _1, _2, etc...) inner-properties, and all other inner-properties the same way, the queryq
(above) would become:... which is clearly incorrect.
The Solution
Now, since we can no longer rely on the tuple-index format (i.e. _1, _2 etc...) to know whether a property should be tokenized, we need a way to identify which properties belong to embedded entities and which ones belong to Ad-Hoc case classes. Since this kind of information is not really related to AST transformations but rather is only used for SQL tokenization, it is a good candidate for AST Opinions.
AST Opinions were first introduced in <> to handle situations where a property is not subject to NamingStrategy manipulation because it is defined in a querySchema (i.e. schema meta). Following, a similar pattern, which Properties are visible or not is introduced to Property opinions (extractable and constructible via
Property.Opinionated
), the actual value of this property is set in the parser (i.e.Parsing.scala
). For exampleProperty.Opinionated(Property.Opinionated(x, "emb", ByStrategy, Hidden), "a", ByStrategy, Visible)
(*) tells us that the propertyemb
is hidden and should not be expressed as part of SQL Tokenization but DOES need to be used for aliasing.Due to these changes, the query mentioned above:
Now produces the correct SQL:
(*) Also notice both properties have
renameable = ByStrategy
, this means that aquerySchema
has been defined for neither of them.README.md
if applicable[WIP]
to the pull request title if it's work in progresssbt scalariformFormat test:scalariformFormat
to make sure that the source files are formatted@getquill/maintainers