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

Reply via email to