private Expression BindBinaryOperatorQueryNode(BinaryOperatorQueryNode binaryOperatorNode) { Expression left = Bind(binaryOperatorNode.Left); Expression right = Bind(binaryOperatorNode.Right); // handle null propagation only if either of the operands can be null bool isNullPropagationRequired = _querySettings.HandleNullPropagation == HandleNullPropagationOption.True && (IsNullable(left.Type) || IsNullable(right.Type)); if (isNullPropagationRequired) { // |----------------------------------------------------------------| // |SQL 3VL truth table. | // |----------------------------------------------------------------| // |p | q | p OR q | p AND q | p = q | // |----------------------------------------------------------------| // |True | True | True | True | True | // |True | False | True | False | False | // |True | NULL | True | NULL | NULL | // |False | True | True | False | False | // |False | False | False | False | True | // |False | NULL | NULL | False | NULL | // |NULL | True | True | NULL | NULL | // |NULL | False | NULL | False | NULL | // |NULL | NULL | Null | NULL | NULL | // |--------|-----------|---------------|---------------|-----------| // before we start with null propagation, convert the operators to nullable if already not. left = ToNullable(left); right = ToNullable(right); bool liftToNull = true; if (left == _nullConstant || right == _nullConstant) { liftToNull = false; } // Expression trees do a very good job of handling the 3VL truth table if we pass liftToNull true. return(CreateBinaryExpression(binaryOperatorNode.OperatorKind, left, right, liftToNull: liftToNull)); } else { return(CreateBinaryExpression(binaryOperatorNode.OperatorKind, left, right, liftToNull: false)); } }
private string BindOperator(BinaryOperatorQueryNode binaryOperatorNode) { var left = binaryOperatorNode.Left is ConstantQueryNode ? MakeSparqlConstant((binaryOperatorNode.Left as ConstantQueryNode).Value) : ProcessNode(binaryOperatorNode.Left); var right = binaryOperatorNode.Right is ConstantQueryNode ? MakeSparqlConstant((binaryOperatorNode.Right as ConstantQueryNode).Value) : ProcessNode(binaryOperatorNode.Right); if (left is VariableIdentifierBinding) { if (binaryOperatorNode.Right is ConstantQueryNode) { var constantValue = (binaryOperatorNode.Right as ConstantQueryNode).Value.ToString(); var uri = (left as VariableIdentifierBinding).IdentifierPrefix + constantValue; switch (binaryOperatorNode.OperatorKind) { case BinaryOperatorKind.Equal: return String.Format("sameTerm(?{0}, <{1}>)", (left as VariableIdentifierBinding).VariableName, uri); case BinaryOperatorKind.NotEqual: return String.Format("!(sameTerm(?{0}, <{1}>)", (left as VariableIdentifierBinding).VariableName, uri); default: throw new NotImplementedException("No support for operator " + binaryOperatorNode.OperatorKind + " over a resource identifier property."); } } throw new NotImplementedException("Identifier properties can only be compared to a constant value"); } switch (binaryOperatorNode.OperatorKind) { // Logical operators case BinaryOperatorKind.Equal: return left + " = " + right; case BinaryOperatorKind.GreaterThan: return left + " > " + right; case BinaryOperatorKind.LessThan: return left + " < " + right; case BinaryOperatorKind.NotEqual: return left + " != " + right; case BinaryOperatorKind.GreaterThanOrEqual: return left + " >= " + right; case BinaryOperatorKind.LessThanOrEqual: return left + " <= " + right; case BinaryOperatorKind.And: return "(" + left + ") && (" + right + ")"; case BinaryOperatorKind.Or: return "(" + left + ") || (" + right + ")"; // Arithmetic operators case BinaryOperatorKind.Add: return "(" + left + " + " + right + ")"; case BinaryOperatorKind.Subtract: return "(" + left + " - " + right + ")"; case BinaryOperatorKind.Multiply: return "(" + left + " * " + right + ")"; case BinaryOperatorKind.Divide: return "(" + left + " / " + right + ")"; case BinaryOperatorKind.Modulo: throw new NotSupportedException("There is no SPARQL equivalent for the OData mod operator"); default: throw new NotImplementedException("No support for " + binaryOperatorNode.OperatorKind); } }
/// <summary> /// Translates a binary operator node. /// </summary> /// <param name="binaryOperatorNode">The binary operator node to translate.</param> /// <returns>Expression which evaluates to the result of the binary operator.</returns> protected virtual Expression TranslateBinaryOperator(BinaryOperatorQueryNode binaryOperatorNode) { ExceptionUtils.CheckArgumentNotNull(binaryOperatorNode, "binaryOperatorNode"); ExceptionUtils.CheckArgumentNotNull(binaryOperatorNode.Left, "binaryOperatorNode.Left"); ExceptionUtils.CheckArgumentNotNull(binaryOperatorNode.Right, "binaryOperatorNode.Right"); Expression left = this.Translate(binaryOperatorNode.Left); Expression right = this.Translate(binaryOperatorNode.Right); // If the operands both statically are null literals we optimize the operator to result in the // null literal as well (per specification) because we otherwise cannot execute such an expression tree if (left == nullLiteralExpression && right == nullLiteralExpression) { // Equality operators have special rules if one (or both) operands are null if (binaryOperatorNode.OperatorKind == BinaryOperatorKind.Equal) { return trueLiteralExpression; } else if (binaryOperatorNode.OperatorKind == BinaryOperatorKind.NotEqual) { return falseLiteralExpression; } return nullLiteralExpression; } Debug.Assert(left != nullLiteralExpression && right != nullLiteralExpression, "None of the operands should be the untyped literal null expression at this point."); // throw if no type is available if (binaryOperatorNode.TypeReference == null) { throw new ODataException(Strings.QueryExpressionTranslator_SingleValueQueryNodeWithoutTypeReference(binaryOperatorNode.Kind)); } // Deal with null propagation; after translating the operands to expressions one (or both) can have changed from a non-nullable type to a // nullable type if null propagation is enabled; compensate for that. if (this.nullPropagationRequired) { HandleBinaryOperatorNullPropagation(ref left, ref right); } else { Debug.Assert(left.Type == right.Type, "left.Type == right.Type"); } // TODO: Deal with open properties // See RequestQueryParser.ExpressionParser.GenerateComparisonExpression for the actual complexity required for comparison operators switch (binaryOperatorNode.OperatorKind) { case BinaryOperatorKind.Or: return Expression.OrElse(left, right); case BinaryOperatorKind.And: return Expression.AndAlso(left, right); case BinaryOperatorKind.Equal: if (left.Type == typeof(byte[])) { return Expression.Equal(left, right, false, DataServiceProviderMethods.AreByteArraysEqualMethodInfo); } else { return Expression.Equal(left, right); } case BinaryOperatorKind.NotEqual: if (left.Type == typeof(byte[])) { return Expression.NotEqual(left, right, false, DataServiceProviderMethods.AreByteArraysNotEqualMethodInfo); } else { return Expression.NotEqual(left, right); } case BinaryOperatorKind.GreaterThan: return CreateGreaterThanExpression(left, right); case BinaryOperatorKind.GreaterThanOrEqual: return CreateGreaterThanOrEqualExpression(left, right); case BinaryOperatorKind.LessThan: return CreateLessThanExpression(left, right); case BinaryOperatorKind.LessThanOrEqual: return CreateLessThanOrEqualExpression(left, right); case BinaryOperatorKind.Add: return Expression.Add(left, right); case BinaryOperatorKind.Subtract: return Expression.Subtract(left, right); case BinaryOperatorKind.Multiply: return Expression.Multiply(left, right); case BinaryOperatorKind.Divide: return Expression.Divide(left, right); case BinaryOperatorKind.Modulo: return Expression.Modulo(left, right); default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.QueryExpressionTranslator_TranslateBinaryOperator_UnreachableCodepath)); } }
private string BindOperator(BinaryOperatorQueryNode binaryOperatorNode) { var left = binaryOperatorNode.Left is ConstantQueryNode ? MakeSparqlConstant((binaryOperatorNode.Left as ConstantQueryNode).Value) : ProcessNode(binaryOperatorNode.Left); var right = binaryOperatorNode.Right is ConstantQueryNode ? MakeSparqlConstant((binaryOperatorNode.Right as ConstantQueryNode).Value) : ProcessNode(binaryOperatorNode.Right); if (left is VariableIdentifierBinding) { if (binaryOperatorNode.Right is ConstantQueryNode) { var constantValue = (binaryOperatorNode.Right as ConstantQueryNode).Value.ToString(); var uri = (left as VariableIdentifierBinding).IdentifierPrefix + constantValue; switch (binaryOperatorNode.OperatorKind) { case BinaryOperatorKind.Equal: return(String.Format("sameTerm(?{0}, <{1}>)", (left as VariableIdentifierBinding).VariableName, uri)); case BinaryOperatorKind.NotEqual: return(String.Format("!(sameTerm(?{0}, <{1}>)", (left as VariableIdentifierBinding).VariableName, uri)); default: throw new NotImplementedException("No support for operator " + binaryOperatorNode.OperatorKind + " over a resource identifier property."); } } throw new NotImplementedException("Identifier properties can only be compared to a constant value"); } switch (binaryOperatorNode.OperatorKind) { // Logical operators case BinaryOperatorKind.Equal: return(left + " = " + right); case BinaryOperatorKind.GreaterThan: return(left + " > " + right); case BinaryOperatorKind.LessThan: return(left + " < " + right); case BinaryOperatorKind.NotEqual: return(left + " != " + right); case BinaryOperatorKind.GreaterThanOrEqual: return(left + " >= " + right); case BinaryOperatorKind.LessThanOrEqual: return(left + " <= " + right); case BinaryOperatorKind.And: return("(" + left + ") && (" + right + ")"); case BinaryOperatorKind.Or: return("(" + left + ") || (" + right + ")"); // Arithmetic operators case BinaryOperatorKind.Add: return("(" + left + " + " + right + ")"); case BinaryOperatorKind.Subtract: return("(" + left + " - " + right + ")"); case BinaryOperatorKind.Multiply: return("(" + left + " * " + right + ")"); case BinaryOperatorKind.Divide: return("(" + left + " / " + right + ")"); case BinaryOperatorKind.Modulo: throw new NotSupportedException("There is no SPARQL equivalent for the OData mod operator"); default: throw new NotImplementedException("No support for " + binaryOperatorNode.OperatorKind); } }