e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
121 lines
4.7 KiB
C#
121 lines
4.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Data.Linq;
|
|
using System.Data.Linq.Provider;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace System.Data.Linq.SqlClient {
|
|
|
|
internal static class SqlExpressionNullability {
|
|
|
|
/// <summary>
|
|
/// Determines whether the given expression may return a null result.
|
|
/// </summary>
|
|
/// <param name="expr">The expression to check.</param>
|
|
/// <returns>null means that it couldn't be determined</returns>
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification="These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
|
|
internal static bool? CanBeNull(SqlExpression expr) {
|
|
switch (expr.NodeType) {
|
|
case SqlNodeType.ExprSet:
|
|
SqlExprSet exprSet = (SqlExprSet)expr;
|
|
return CanBeNull(exprSet.Expressions);
|
|
|
|
case SqlNodeType.SimpleCase:
|
|
SqlSimpleCase sc = (SqlSimpleCase)expr;
|
|
return CanBeNull(sc.Whens.Select(w => w.Value));
|
|
|
|
case SqlNodeType.Column:
|
|
SqlColumn col = (SqlColumn)expr;
|
|
if (col.MetaMember != null) {
|
|
return col.MetaMember.CanBeNull;
|
|
}
|
|
else if (col.Expression != null) {
|
|
return CanBeNull(col.Expression);
|
|
}
|
|
return null; // Don't know.
|
|
|
|
case SqlNodeType.ColumnRef:
|
|
SqlColumnRef cref = (SqlColumnRef)expr;
|
|
return CanBeNull(cref.Column);
|
|
|
|
case SqlNodeType.Value:
|
|
return ((SqlValue)expr).Value == null;
|
|
|
|
case SqlNodeType.New:
|
|
case SqlNodeType.Multiset:
|
|
case SqlNodeType.Grouping:
|
|
case SqlNodeType.DiscriminatedType:
|
|
case SqlNodeType.IsNotNull: // IsNull\IsNotNull always return true or false and can never return NULL.
|
|
case SqlNodeType.IsNull:
|
|
case SqlNodeType.Exists:
|
|
return false;
|
|
|
|
case SqlNodeType.Add:
|
|
case SqlNodeType.Sub:
|
|
case SqlNodeType.Mul:
|
|
case SqlNodeType.Div:
|
|
case SqlNodeType.Mod:
|
|
case SqlNodeType.BitAnd:
|
|
case SqlNodeType.BitOr:
|
|
case SqlNodeType.BitXor:
|
|
case SqlNodeType.Concat: {
|
|
SqlBinary bop = (SqlBinary)expr;
|
|
bool? left = CanBeNull(bop.Left);
|
|
bool? right = CanBeNull(bop.Right);
|
|
return (left != false) || (right != false);
|
|
}
|
|
|
|
case SqlNodeType.Negate:
|
|
case SqlNodeType.BitNot: {
|
|
SqlUnary uop = (SqlUnary)expr;
|
|
return CanBeNull(uop.Operand);
|
|
}
|
|
|
|
case SqlNodeType.Lift: {
|
|
SqlLift lift = (SqlLift)expr;
|
|
return CanBeNull(lift.Expression);
|
|
}
|
|
|
|
case SqlNodeType.OuterJoinedValue:
|
|
return true;
|
|
|
|
default:
|
|
return null; // Don't know.
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to determine nullability for a collection of expressions.
|
|
/// * If at least one of the expressions is nullable, the collection is nullable.
|
|
/// * If no expressions are nullable, but at least one is 'don't know', the collection is 'don't know'.
|
|
/// * Otherwise all expressions are non-nullable and the nullability is false.
|
|
/// </summary>
|
|
private static bool? CanBeNull(IEnumerable<SqlExpression> exprs) {
|
|
bool hasAtleastOneUnknown = false;
|
|
foreach(SqlExpression e in exprs) {
|
|
bool? nullability = CanBeNull(e);
|
|
|
|
// Even one expression that could return null means the
|
|
// collection can return null.
|
|
if (nullability == true)
|
|
return true;
|
|
|
|
// If there is one or more 'unknown' and no definitely nullable
|
|
// results then the collection nullability is 'unknown'.
|
|
if (nullability == null)
|
|
hasAtleastOneUnknown = true;
|
|
}
|
|
if (hasAtleastOneUnknown)
|
|
return null;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|