Just to bring this old and long thread back to life for a brief
moment, I committed my new version of the algorithm and resolved the
issue.  I'm very thankful for the work that Harald did to make this
happen... especially in framing the important question of whether a
member expression will produce true if emptily outer joined.  I've
been looking at this problem on and off since the start of this
thread, and after a few mornings of insight (after several evenings of
head bashing), I was able to make the connection to an easy to
understand solution to the problem, which has the potential for even
inner join optimization in even complex scenarios.

So in summary... thanks Harald... and I think we're done with this one
for now. :)

        Patrick Earl

On Sun, Apr 24, 2011 at 3:45 PM, Fabio Maulo <[email protected]> wrote:
> Just a very minus-little..
> Patrick,
> That work is may be "managed" by query-substitution (true -> 1), we should
> never use 0 for false and 1 for true during the conversion of a Linq to HQL.
> The Linq-provider should work with true/false and the final Hql2Sql
> translator will do the replacement.
> On Sun, Apr 24, 2011 at 6:19 PM, Patrick Earl <[email protected]> wrote:
>>
>> On Sun, Apr 24, 2011 at 5:02 AM, Harald Mueller <[email protected]>
>> wrote:
>> > Maybe I need to stress the following once more: What we try here, is to
>> > "put 3-valued logic ('SQL logic') over navigational expressions ('Linq
>> > expressions')". So it is a *new semantics* for Linq expressions. As I have
>> > pointed out repeatedly, I personally would have liked to "force"
>> > Linq2Objects logic into NHib.Linq expressions. However, we agreed that we
>> > want "simple implementations" by way of using SQL logic as much as 
>> > possible.
>> > But when we do this, let's do it in earnest! That means, above all, clearly
>> > separating *expressions* and *conditions* in our analysis. Of course, Linq
>> > expressions do not have this separation - so we need special rules to
>> > distinguish them. Right now, in the code, we loosely rule that expressions
>> > with result type bool are conditions, unless they are leaf expressions, in
>> > which case they are interpreted as conditions of the form expr == 1. Let's
>> > stick to this at least for some more time.
>> >
>> > (This ruling does most certainly not work in cases like (a == b) != (c <
>> > d), where a direct transformation is simply not allowed in SQL - and
>> > probably also in HQL. But such "cross-overs" between expressions and
>> > conditions are a different topic - right now, I just propose that we do not
>> > support them).
>>
>>
>> Just a couple minor notes.  It's more like expr == <true> since some
>> databases actually have a boolean type.  Additionally, it should
>> ultimately be a dialect-specific transformation to decide how the SQL
>> for these booleans is ultimately generated.  That work is avoided for
>> the time being.  Additionally, (a==b) != (c<d) is actually a valid
>> expression in databases like PostgreSQL and SQLite.  It is actually
>> rather unfortunate that MSSQL opted not to have a boolean type.  Now
>> we need to perform crazy hacks like "case when x = y then 1 else 0" :(
>>  I don't think it's unreasonable that Linq will actually transform
>> expressions like (a==b) != (c<d) into valid case statements at some
>> point.
>>
>>
>> > (e) expr <op> <constant(s)> (where <constant(s)> are not value-nulls)
>> >
>> > Old result: { mE -> n } for all joined mE's in expr.
>> >
>> > New result:
>> > - If an outer-joined mE makes the expression expr null (we have u in the
>> > expression mapping), it makes the condition n.
>> > - If outer-joining that mE makes the expression not-null (we have v in
>> > the expression), the condition can become t or f, but also n. The latter
>> > happens if *another* member expression, when outer joined, makes the
>> > expression null.
>> >
>> > Concisely:
>> >    { mE -> n }         if { mE -> u } in expr
>> >    { mE -> tnf }       if { mE -> v } in expr
>>
>>
>> Wouldn't it be tf?
>>
>>
>> > Case (k):
>> > Constants: Constants do not contain any member expressions, so the
>> > mapping comes out empty.
>>
>> Much of this section you already discuss below, but I wrote this part
>> without reading your remaining comments.
>>
>> When determining the result of an operator, it's handy to have the
>> result of the constant around.  I was considering three possible
>> values for expressions based on your original design:
>>
>> oj-null
>> value-null
>> value-not-null
>>
>> It would technically even be possible to propagate constants as far as
>> possible to optimize out known expression results.  For example, 1+1 =
>> 2 could be T instead of TF.  While we're ultimately only interested in
>> how to join the member expressions, there's significant intermediate
>> information available in the expressions.  Providing a framework for
>> taking advantage of that information may be helpful.
>>
>> I've also been thinking about how to avoid accidental inner joins for
>> partially supported cases.  I've an idea about wrapping
>> VisitExpression and ensuring that the visitor marks the result as
>> handled.  Sadly there's no technique that can avoid errors in
>> development. :)
>>
>>
>> > Case (l) ["ell"]:
>> > Dot operator on complete expressions: (expr).P - this could be something
>> > like (a.B ?? a.D).E or worse. I think we should not support such 
>> > expressions
>> > (i.e., we allow wrong results); or at least should not support inner join
>> > optimization for them! In the latter case, we have to invest some thought -
>> > we'd have to remove all those mappings. Here is a suggestion to solve this
>> > easily:
>> >
>> >    We replace all mapped values with the pessimistic assumption uv.
>>
>>
>> This is an interesting case.  I wonder if the code produces joins for
>> this right now.
>>
>>
>> > Of course, we *could* implement that "possible expression value"
>> > concept. In the following, I use pairs <M,Z> as expression results, where M
>> > is the mapping as above and Z is the set of all possible expression values.
>> > The Z set can then be intersected with all mapping results to possibly
>> > reduce the mappings. I do not give the complete formalism (which requires
>> > all the case tables above to be rewritten to add those "possible values"),
>> > but just an outline:
>>
>>
>> Ya, this is how it would need to be designed for maximum flexibility.
>> I've also got another stack design up my sleeves to eliminate the need
>> for the separate left/right traversals currently utilized.  Basically
>> each expression node would push a single result onto the stack.  In a
>> binary expression, it would pop two off and push one back on.
>>
>>
>> I'll be working on this this afternoon.  I hope it comes out in a
>> concise manner.
>>
>> Thanks for all your great thoughts Harald!  I especially liked the
>> (expr).P example.
>>
>>        Patrick Earl
>
>
>
> --
> Fabio Maulo
>
>

Reply via email to