private Predicate ImplementEquality(Predicate left, Predicate right, EqualsPattern pattern) { switch (left.PredicateType) { case PredicateType.Constant: switch (right.PredicateType) { case PredicateType.Constant: // constant EQ constant return(left.Equal(right)); case PredicateType.Null: // null EQ constant --> false return(PredicateBuilder.False); default: return(ImplementEqualityConstantAndUnknown((ConstantPredicate)left, right, pattern)); } case PredicateType.Null: switch (right.PredicateType) { case PredicateType.Constant: return(PredicateBuilder.False); case PredicateType.Null: return(PredicateBuilder.True); default: return(right.IsNull()); } default: switch (right.PredicateType) { case PredicateType.Constant: return(ImplementEqualityConstantAndUnknown((ConstantPredicate)right, left, pattern)); case PredicateType.Null: return(left.IsNull()); default: return(ImplementEqualityUnknownArguments(left, right, pattern)); } } }
private Predicate ImplementEqualityUnknownArguments(Predicate left, Predicate right, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: return(left.Equal(right)); case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins return(left.Equal(right).Or(left.IsNull().And(right.IsNull()))); case EqualsPattern.PositiveNullEqualityComposable: { var bothNotNull = left.Equal(right); var bothNull = left.IsNull().And(right.IsNull()); var anyOneIsNull = left.IsNull().Or(right.IsNull()); return((bothNotNull.And(anyOneIsNull.Not())).Or(bothNull)); } default: return(null); } }
private Predicate ImplementEqualityConstantAndUnknown(ConstantPredicate constant, Predicate unknown, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins return(constant.Equal(unknown)); case EqualsPattern.PositiveNullEqualityComposable: return(constant.Equal(unknown).And(unknown.IsNull().Not())); default: return(null); } }
private Predicate RecursivelyRewriteEqualsExpression(Predicate left, Predicate right, EqualsPattern pattern) { var leftType = left.ResultType.EdmType as RowType; var rightType = right.ResultType.EdmType as RowType; if (null != leftType || null != rightType) { if (null != leftType && null != rightType) { Predicate shreddedEquals = null; foreach (var property in leftType.Properties) { var leftElement = left.Property(property); var rightElement = right.Property(property); var elementsEquals = RecursivelyRewriteEqualsExpression(leftElement, rightElement, pattern); if (null == shreddedEquals) { shreddedEquals = elementsEquals; } else { shreddedEquals = shreddedEquals.And(elementsEquals); } } return(shreddedEquals); } else { return(PredicateBuilder.False); } } else { return(ImplementEquality(left, right, pattern)); } }
// Generate an equality expression where the values of the left and right operands are completely unknown private DbExpression ImplementEqualityUnknownArguments(DbExpression left, DbExpression right, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: // left EQ right return left.Equal(right); case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins return left.Equal(right).Or(left.IsNull().And(right.IsNull())); case EqualsPattern.PositiveNullEqualityComposable: { var bothNotNull = left.Equal(right); var bothNull = left.IsNull().And(right.IsNull()); if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior) { return bothNotNull.Or(bothNull); // same as EqualsPattern.PositiveNullEqualityNonComposable } // add more logic to avoid undefined result for true clr semantics, ensuring composability // (left EQ right AND NOT (left IS NULL OR right IS NULL)) OR (left IS NULL AND right IS NULL) var anyOneIsNull = left.IsNull().Or(right.IsNull()); return (bothNotNull.And(anyOneIsNull.Not())).Or(bothNull); } default: Debug.Fail("unexpected pattern"); return null; } }
internal Predicate CreateEqualsExpression(Predicate left, Predicate right, EqualsPattern pattern, Type leftClrType, Type rightClrType) { return(RecursivelyRewriteEqualsExpression(left, right, pattern)); }
// Generate an equality expression with one unknown operator and private DbExpression ImplementEqualityConstantAndUnknown( DbConstantExpression constant, DbExpression unknown, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins return constant.Equal(unknown); // either both are non-null, or one is null and the predicate result is undefined case EqualsPattern.PositiveNullEqualityComposable: if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior) { return constant.Equal(unknown); // same as EqualsPattern.PositiveNullEqualityNonComposable } return constant.Equal(unknown).And(unknown.IsNull().Not()); // add more logic to avoid undefined result for true clr semantics default: Debug.Fail("unknown pattern"); return null; } }
// For comparisons, where the left and right side are nullable or not nullable, // here are the (compositionally safe) null equality predicates: // -- x NOT NULL, y NULL // x = y AND NOT (y IS NULL) // -- x NULL, y NULL // (x = y AND (NOT (x IS NULL OR y IS NULL))) OR (x IS NULL AND y IS NULL) // -- x NOT NULL, y NOT NULL // x = y // -- x NULL, y NOT NULL // x = y AND NOT (x IS NULL) private DbExpression ImplementEquality(DbExpression left, DbExpression right, EqualsPattern pattern) { switch (left.ExpressionKind) { case DbExpressionKind.Constant: switch (right.ExpressionKind) { case DbExpressionKind.Constant: // constant EQ constant return left.Equal(right); case DbExpressionKind.Null: // null EQ constant --> false return DbExpressionBuilder.False; default: return ImplementEqualityConstantAndUnknown((DbConstantExpression)left, right, pattern); } case DbExpressionKind.Null: switch (right.ExpressionKind) { case DbExpressionKind.Constant: // null EQ constant --> false return DbExpressionBuilder.False; case DbExpressionKind.Null: // null EQ null --> true return DbExpressionBuilder.True; default: // null EQ right --> right IS NULL return right.IsNull(); } default: // unknown switch (right.ExpressionKind) { case DbExpressionKind.Constant: return ImplementEqualityConstantAndUnknown((DbConstantExpression)right, left, pattern); case DbExpressionKind.Null: // left EQ null --> left IS NULL return left.IsNull(); default: return ImplementEqualityUnknownArguments(left, right, pattern); } } }
private DbExpression RecursivelyRewriteEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern) { // check if either side is an initializer type var leftType = left.ResultType.EdmType as RowType; var rightType = right.ResultType.EdmType as RowType; if (null != leftType || null != rightType) { if (null != leftType && null != rightType) { DbExpression shreddedEquals = null; // if the types are the same, use struct equivalence semantics foreach (var property in leftType.Properties) { var leftElement = left.Property(property); var rightElement = right.Property(property); var elementsEquals = RecursivelyRewriteEqualsExpression( leftElement, rightElement, pattern); // build up and expression if (null == shreddedEquals) { shreddedEquals = elementsEquals; } else { shreddedEquals = shreddedEquals.And(elementsEquals); } } return shreddedEquals; } else { // if one or both sides is an initializer and the types are not the same, // "equals" always evaluates to false return DbExpressionBuilder.False; } } else { return _funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior ? ImplementEquality(left, right, EqualsPattern.Store) : ImplementEquality(left, right, pattern); } }
// <summary> // Creates an implementation of equals using the given pattern. Throws exception when argument types // are not supported for equals comparison. // </summary> private DbExpression CreateEqualsExpression( DbExpression left, DbExpression right, EqualsPattern pattern, Type leftClrType, Type rightClrType) { VerifyTypeSupportedForComparison(leftClrType, left.ResultType, null); VerifyTypeSupportedForComparison(rightClrType, right.ResultType, null); //For Ref Type comparison, check whether they refer to compatible Entity Types. var leftType = left.ResultType; var rightType = right.ResultType; if (leftType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType && rightType.EdmType.BuiltInTypeKind == BuiltInTypeKind.RefType) { TypeUsage commonType; if (!TypeSemantics.TryGetCommonType(leftType, rightType, out commonType)) { var leftRefType = left.ResultType.EdmType as RefType; var rightRefType = right.ResultType.EdmType as RefType; throw new NotSupportedException( Strings.ELinq_UnsupportedRefComparison(leftRefType.ElementType.FullName, rightRefType.ElementType.FullName)); } } return RecursivelyRewriteEqualsExpression(left, right, pattern); }
private DbExpression RecursivelyRewriteEqualsExpression(DbExpression left, DbExpression right, EqualsPattern pattern) { // check if either side is an initializer type RowType leftType = left.ResultType.EdmType as RowType; RowType rightType = left.ResultType.EdmType as RowType; if (null != leftType || null != rightType) { if ((null != leftType && null != rightType) && leftType.EdmEquals(rightType)) { DbExpression shreddedEquals = null; // if the types are the same, use struct equivalence semantics foreach (EdmProperty property in leftType.Properties) { DbPropertyExpression leftElement = left.Property(property); DbPropertyExpression rightElement = right.Property(property); DbExpression elementsEquals = RecursivelyRewriteEqualsExpression( leftElement, rightElement, pattern); // build up and expression if (null == shreddedEquals) { shreddedEquals = elementsEquals; } else { shreddedEquals = shreddedEquals.And(elementsEquals); } } return shreddedEquals; } else { // if one or both sides is an initializer and the types are not the same, // "equals" always evaluates to false return DbExpressionBuilder.False; } } else { return ImplementEquality(left, right, pattern); } }