Hey Steve,
Am 04.08.2016 um 15:52 schrieb Steve Ebersole:
Hey Christian,
In general terms, one of the items on the docket for SQM is better
TREAT support; but there is a lot that goes into that statement. One
aspect is what we support in the parser properly in terms of
recognition. Another is how this translates into the generates SQL
query. All of this is being looked at with SQM.
Also, relatedly, better support for switching on/off JPQL-compliance
checking is a goal.
<background>
TREAT is an explicit downcast operator. It allows you to down cast a
type reference in order to reference one of its subclass attributes.
HQL actually supports implicit downcasting (subclass attribute
references). In a way you can think of HQL as offering TREAT support
implicitly.
</background>
With all that in mind, see my comments inline...
On Wed, Aug 3, 2016 at 9:43 PM Christian Beikov
<christian.beikov(a)gmail.com <mailto:christian.beikov@gmail.com>> wrote:
* Missing support for treated paths like "TREAT(...).property"
in the
parser. This is more or less workaroundable for non-root path
treats. Since root paths can't be treat joined to my
knowledge, some
treated paths simple can't be used. Maybe in 90% of the cases
hibernate will just resolve to the right property without needing
the treat?
I'm not sure what you mean by "root paths can't be treated". You mean
literally the query root? As in "MyEntity e" in "select e from
MyEntity e"? If so, yes JPA does not allow for that to be TREAT'ed;
but really it makes no sense to allow TREAT there, if you step back
and look at it. A query like "select e from TREAT(MyEntity as MySub)
e" makes no sense; its ultimately just the same as "select e from
MySub e". Arguably I guess it *could* make some sense *if* MySub is a
sub MappedSuperclass since technically JPA also allows only entities
as query roots (HQL does not have this restriction).
What I meant by that is what
is shown in the test "selectTreatedRoot"
which would be a query like "SELECT TREAT(e AS SubEntity).subProperty
FROM MyEntity e". JPA does not specifically allow TREAT in the SELECT
clause but in the WHERE clause, and Hibernate does not seem seem to
support either. Also see:
https://hibernate.atlassian.net/browse/HHH-10988
As discussed above, assuming that "someProp" is a persistent property
defined on MySub, this is a perfectly valid HQL: "select e.someProp
from MyEntity e". It is not however valid JPQL; in JPQL you'd have to
say: "select treat( e as MySub).someProp from MyEntity e"
That's what
I mean with 90% of the cases.
All that said, the inability to dereference a TREAT'ed path (the
".property" part) would be a bug, and tbh I am surprised that you say
this does not work with Hibernate. Which of your tests show this not
working? Is there a related Jira?
See above. There are tests for the select clause
in my test project but
I'd expect treat to work for all clauses.
* Missing support for a special case of treat join which apparently
was overseen by the JPA spec which would be
"TREAT(TREAT(rootPath AS
Subtype).relation AS RelationSubtype)". Eclipselink not only
allows
this syntax but for inner joins also implements the appropriate
logic. I am thinking that this might even work in hibernate
without
the inner treat, but would fail for attributes with same names.
Do you mean specifically the nesting of TREAT operators? Again, I am
surprised that this would not work. Tests? Jira?
The test
"treatJoinTreatedRoot" would show that it's not working. Also
see the issue:
https://hibernate.atlassian.net/browse/HHH-9345
* Joins for supertypes and conditions on discriminators are not
aware
of the join type actually used in the treated join => left join
should cascade up the type hierarchy and discriminator checks
should
contain OR DTYPE IS NULL for left join semantics. If treats are in
subconditions the DTYPE checks might even be wrong
This "restriction" aspect is honestly Hibernate's biggest shortcoming
in its TREAT support. Hibernate's "SQL generation machinery" simply
was not built to support this. At all. I hacked together some support
for TREAT on top of that legacy "SQL generation machinery", but it is
very limited.
A test case that shows a problem is
"selectTreatJoinedRelation" which
does an inner join on Sub1 but left join on Sub2 although the join is a
left join treated with Sub1. Apart from not expecting Sub2 to be joined
in that case, the join type is wrong for Sub1. I will formulate how I
think treat should be translated to SQL and let you know about that.
This aspect is specifically what we have discussed in terms of TREAT
support improvements in SQM. Keeping track of where the TREAT occurs
so that we can later know how to properly handle it, whether that
means we can skip parts of the inheritance join tree or maybe need to
render some extra WHERE-clause restrictions...
* Joins are generated for the whole hierarchy not only the types
that
are treated => this might seem to be "just" a performance problem,
but IMO actually is a correctness problem as it makes a difference
if you get e.g. 2 rows or 2 * TYPES rows especially for count
queries.
Generating the joins for the whole hierarchy is important for HQL's
implicit downcasting support. However, that said, there are attempts
to restrict that already in place. IIRC this only works for TREATs
that occur in the FROM-clause specifically. However, I thought that
other TREAT operations were still handled in terms of adding
restrictions when dictated. So this might be specific to certain
circumstances.
To be honest, I rethought the whole treat support and actually I
think
treat shouldn't result in filtering at all. I will give you a short
update on this regard when I formulated what I would expect from treat
exactly as the JPA spec is IMO quite ambiguous.
Again, which specific tests show this? Jira?
----
Some specifics of the plan...
First, internally within the query AST I want to make sure that we
model *all* downcasts whether they are implicit or explicit. In other
words, in terms of AST both of these queries would result in the same
structure:
1. select e.someProp from MyEntity
2. select treat(e as MySub).someProp from MyEntity
Secondly, we need to keep track of various pieces of information
pertaining to a downcast
(see org.hibernate.sqm.query.from.Downcast/org.hibernate.sqm.query.from.Downcastable).
This includes info like:
1. The downcast "target"
2. The context in which the downcast occurred
3. Any/all contexts in which the downcast is used.
The last 2 items there would specifically lead to:
* Which specific inheritance joins are needed - and may indicate
join type
* Any extra restrictions we may need to add