//---------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
namespace System.Data.SqlClient.SqlGen
{
using System;
using System.Collections.Generic;
using System.Data.Common.CommandTrees;
///
/// The Sql8ConformanceChecker walks a DbExpression tree and determines whether
/// it should be rewritten in order to be translated to SQL appropriate for SQL Server 2000.
/// The tree should be rewritten if it contains any of the following expressions:
///
///
///
///
///
///
/// Also, it throws if it determines that the tree can not
/// be translated into SQL appropriate for SQL Server 2000.
/// This happens if:
///
/// - The tree contains
/// - The tree contains with property Limit of type
/// - The tree contains with property Count of type
///
///
/// The visitor only checks for expressions for which the support differs between SQL Server 2000 and SQL Server 2005,
/// but does not check/throw for expressions that are not supported for both providers.
///
/// Implementation note: In the cases when the visitor encounters an expression that requires rewrite,
/// it still needs to walk its structure in case something below it is not supported and needs to throw.
///
///
internal class Sql8ConformanceChecker : DbExpressionVisitor
{
#region 'Public' API
///
/// The entry point
///
///
/// True if the tree needs to be rewriten, false otherwise
internal static bool NeedsRewrite(DbExpression expr)
{
Sql8ConformanceChecker checker = new Sql8ConformanceChecker();
return expr.Accept(checker);
}
#endregion
#region Constructor
///
/// Default Constructor
///
private Sql8ConformanceChecker()
{
}
#endregion
#region Visitor Helpers
///
/// Default handling for DbUnaryExpression-derived classes. Simply visits its argument
///
/// The DbUnaryExpression to visit
///
private bool VisitUnaryExpression(DbUnaryExpression expr)
{
return VisitExpression(expr.Argument);
}
///
/// Default handling for DbBinaryExpression-derived classes. Visits both arguments.
///
/// The DbBinaryExpression to visit
///
private bool VisitBinaryExpression(DbBinaryExpression expr)
{
bool leftNeedsRewrite = VisitExpression(expr.Left);
bool rightNeedsRewrite = VisitExpression(expr.Right);
return leftNeedsRewrite || rightNeedsRewrite;
}
///
/// Used for
///
///
///
///
private delegate bool ListElementHandler(TElementType element);
///
/// Walks the structure
///
///
///
private bool VisitAggregate(DbAggregate aggregate)
{
return VisitExpressionList(aggregate.Arguments);
}
///
/// DbExpressionBinding handler
///
///
///
private bool VisitExpressionBinding(DbExpressionBinding expressionBinding)
{
return VisitExpression(expressionBinding.Expression);
}
///
/// Used as handler for expressions
///
///
///
private bool VisitExpression(DbExpression expression)
{
if (expression == null)
{
return false;
}
return expression.Accept(this);
}
///
/// Used as handler for SortClauses
///
///
///
private bool VisitSortClause(DbSortClause sortClause)
{
return VisitExpression(sortClause.Expression);
}
///
/// Helper method for iterating a list
///
///
///
///
///
private static bool VisitList(ListElementHandler handler, IList list)
{
bool result = false;
foreach (TElementType element in list)
{
bool localResult = handler(element);
result = result || localResult;
}
return result;
}
///
/// Handing for list of s.
///
///
///
private bool VisitAggregateList(IList list)
{
return VisitList(VisitAggregate, list);
}
///
/// Handing for list of s.
///
///
///
private bool VisitExpressionBindingList(IList list)
{
return VisitList(VisitExpressionBinding, list);
}
///
/// Handing for list of s.
///
///
///
private bool VisitExpressionList(IList list)
{
return VisitList(VisitExpression, list);
}
///
/// Handling for list of s.
///
///
///
private bool VisitSortClauseList(IList list)
{
return VisitList(VisitSortClause, list);
}
#endregion
#region DbExpressionVisitor Members
///
/// Called when an of an otherwise unrecognized type is encountered.
///
/// The expression
///
/// Always thrown if this method is called, since it indicates that is of an unsupported type
public override bool Visit(DbExpression expression)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.Cqt_General_UnsupportedExpression(expression.GetType().FullName));
}
///
///
///
/// The DbAndExpression that is being visited.
///
public override bool Visit(DbAndExpression expression)
{
return VisitBinaryExpression(expression);
}
///
/// Not supported on SQL Server 2000.
///
/// The DbApplyExpression that is being visited.
/// Always
public override bool Visit(DbApplyExpression expression)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.SqlGen_ApplyNotSupportedOnSql8);
}
///
/// Default handling for DbArithmeticExpression. Visits all arguments.
///
/// The DbArithmeticExpression that is being visited.
///
public override bool Visit(DbArithmeticExpression expression)
{
return VisitExpressionList(expression.Arguments);
}
///
/// Walks the strucutre
///
/// The DbCaseExpression that is being visited.
///
public override bool Visit(DbCaseExpression expression)
{
bool whenNeedsRewrite = VisitExpressionList(expression.When);
bool thenNeedsRewrite = VisitExpressionList(expression.Then);
bool elseNeedsRewrite = VisitExpression(expression.Else);
return whenNeedsRewrite || thenNeedsRewrite || elseNeedsRewrite;
}
///
///
///
/// The DbCastExpression that is being visited.
///
public override bool Visit(DbCastExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbComparisonExpression that is being visited.
///
public override bool Visit(DbComparisonExpression expression)
{
return VisitBinaryExpression(expression);
}
///
/// Returns false
///
/// The DbConstantExpression that is being visited.
///
public override bool Visit(DbConstantExpression expression)
{
return false;
}
///
/// Walks the structure
///
/// The DbCrossJoinExpression that is being visited.
///
public override bool Visit(DbCrossJoinExpression expression)
{
return VisitExpressionBindingList(expression.Inputs);
}
///
///
///
/// The DeRefExpression that is being visited.
///
public override bool Visit(DbDerefExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbDistinctExpression that is being visited.
///
public override bool Visit(DbDistinctExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbElementExpression that is being visited.
///
public override bool Visit(DbElementExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbEntityRefExpression that is being visited.
///
public override bool Visit(DbEntityRefExpression expression)
{
return VisitUnaryExpression(expression);
}
///
/// Returns true, the tree needs to be rewritten.
///
/// The DbExceptExpression that is being visited.
///
public override bool Visit(DbExceptExpression expression)
{
//Walk the structure in case a non-supported construct is encountered
VisitExpression(expression.Left);
VisitExpression(expression.Right);
return true;
}
///
/// Walks the structure
///
/// The DbFilterExpression that is being visited.
///
public override bool Visit(DbFilterExpression expression)
{
bool inputNeedsRewrite = VisitExpressionBinding(expression.Input);
bool predicateNeedsRewrite = VisitExpression(expression.Predicate);
return inputNeedsRewrite || predicateNeedsRewrite;
}
///
/// Visits the arguments
///
/// The DbFunctionExpression that is being visited.
///
public override bool Visit(DbFunctionExpression expression)
{
return VisitExpressionList(expression.Arguments);
}
///
/// Visits the arguments and lambda body
///
/// The DbLambdaExpression that is being visited.
///
public override bool Visit(DbLambdaExpression expression)
{
bool argumentsNeedRewrite = VisitExpressionList(expression.Arguments);
bool bodyNeedsRewrite = VisitExpression(expression.Lambda.Body);
return argumentsNeedRewrite || bodyNeedsRewrite;
}
///
/// Walks the structure
///
/// The DbExpression that is being visited.
///
public override bool Visit(DbGroupByExpression expression)
{
bool inputNeedsRewrite = VisitExpression(expression.Input.Expression);
bool keysNeedRewrite = VisitExpressionList(expression.Keys);
bool aggregatesNeedRewrite = VisitAggregateList(expression.Aggregates);
return inputNeedsRewrite || keysNeedRewrite || aggregatesNeedRewrite;
}
///
/// Returns true.
///
/// The DbIntersectExpression that is being visited.
///
public override bool Visit(DbIntersectExpression expression)
{
//Walk the structure in case a non-supported construct is encountered
VisitExpression(expression.Left);
VisitExpression(expression.Right);
return true;
}
///
///
///
/// The DbIsEmptyExpression that is being visited.
///
public override bool Visit(DbIsEmptyExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbIsNullExpression that is being visited.
///
public override bool Visit(DbIsNullExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbIsOfExpression that is being visited.
///
public override bool Visit(DbIsOfExpression expression)
{
return VisitUnaryExpression(expression);
}
///
/// Walks the structure
///
/// The DbJoinExpression that is being visited.
///
public override bool Visit(DbJoinExpression expression)
{
bool leftNeedsRewrite = VisitExpressionBinding(expression.Left);
bool rightNeedsRewrite = VisitExpressionBinding(expression.Right);
bool conditionNeedsRewrite = VisitExpression(expression.JoinCondition);
return leftNeedsRewrite || rightNeedsRewrite || conditionNeedsRewrite;
}
///
/// Walks the structure
///
/// The DbLikeExpression that is being visited.
///
public override bool Visit(DbLikeExpression expression)
{
bool argumentNeedsRewrite = VisitExpression(expression.Argument);
bool patternNeedsRewrite = VisitExpression(expression.Pattern);
bool excapeNeedsRewrite = VisitExpression(expression.Escape);
return argumentNeedsRewrite || patternNeedsRewrite || excapeNeedsRewrite;
}
///
/// Walks the structure
///
///
///
/// expression.Limit is DbParameterReferenceExpression
public override bool Visit(DbLimitExpression expression)
{
if (expression.Limit is DbParameterReferenceExpression)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.SqlGen_ParameterForLimitNotSupportedOnSql8);
}
return VisitExpression(expression.Argument);
}
#if METHOD_EXPRESSION
///
/// Visitor pattern method for .
///
/// The MethodExpression that is being visited.
/// is null
public override bool Visit(MethodExpression expression)
{
bool result = VisitExpressionList(expression.Arguments);
if (expression.Instance != null)
{
bool instanceNeedsRewrite = VisitExpression(expression.Instance);
result = result || instanceNeedsRewrite;
}
return result;
}
#endif
///
/// Walks the arguments
///
/// The DbNewInstanceExpression that is being visited.
///
public override bool Visit(DbNewInstanceExpression expression)
{
return VisitExpressionList(expression.Arguments);
}
///
///
///
/// The DbNotExpression that is being visited.
///
public override bool Visit(DbNotExpression expression)
{
return VisitUnaryExpression(expression);
}
///
/// Returns false
///
/// The DbNullExpression that is being visited.
/// false
public override bool Visit(DbNullExpression expression)
{
return false;
}
///
///
///
/// The DbOfTypeExpression that is being visited.
///
public override bool Visit(DbOfTypeExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbOrExpression that is being visited.
///
public override bool Visit(DbOrExpression expression)
{
return VisitBinaryExpression(expression);
}
///
/// Returns false
///
/// The DbParameterReferenceExpression that is being visited.
///
public override bool Visit(DbParameterReferenceExpression expression)
{
return false;
}
///
/// Walks the structure
///
/// The DbProjectExpression that is being visited.
///
public override bool Visit(DbProjectExpression expression)
{
bool inputNeedsRewrite = VisitExpressionBinding(expression.Input);
bool projectionNeedsRewrite = VisitExpression(expression.Projection);
return inputNeedsRewrite || projectionNeedsRewrite;
}
///
/// Returns false
///
/// The DbPropertyExpression that is being visited.
///
public override bool Visit(DbPropertyExpression expression)
{
return VisitExpression(expression.Instance);
}
///
/// Walks the structure
///
/// The DbQuantifierExpression that is being visited.
///
public override bool Visit(DbQuantifierExpression expression)
{
bool inputNeedsRewrite = VisitExpressionBinding(expression.Input);
bool predicateNeedsRewrite = VisitExpression(expression.Predicate);
return inputNeedsRewrite || predicateNeedsRewrite;
}
///
///
///
/// The DbRefExpression that is being visited.
///
public override bool Visit(DbRefExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbRefKeyExpression that is being visited.
///
public override bool Visit(DbRefKeyExpression expression)
{
return VisitUnaryExpression(expression);
}
///
/// Walks the structure
///
/// The DbRelationshipNavigationExpression that is being visited.
///
public override bool Visit(DbRelationshipNavigationExpression expression)
{
return VisitExpression(expression.NavigationSource);
}
///
/// Returns false;
///
/// The DbScanExpression that is being visited.
///
public override bool Visit(DbScanExpression expression)
{
return false;
}
///
/// Resturns true
///
///
///
/// expression.Count is DbParameterReferenceExpression
public override bool Visit(DbSkipExpression expression)
{
if (expression.Count is DbParameterReferenceExpression)
{
throw EntityUtil.NotSupported(System.Data.Entity.Strings.SqlGen_ParameterForSkipNotSupportedOnSql8);
}
//Walk the structure in case a non-supported construct is encountered
VisitExpressionBinding(expression.Input);
VisitSortClauseList(expression.SortOrder);
VisitExpression(expression.Count);
return true;
}
///
/// Walks the structure
///
/// The DbSortExpression that is being visited.
///
public override bool Visit(DbSortExpression expression)
{
bool inputNeedsRewrite = VisitExpressionBinding(expression.Input);
bool sortClauseNeedsRewrite = VisitSortClauseList(expression.SortOrder);
return inputNeedsRewrite || sortClauseNeedsRewrite;
}
///
///
///
/// The DbTreatExpression that is being visited.
///
public override bool Visit(DbTreatExpression expression)
{
return VisitUnaryExpression(expression);
}
///
///
///
/// The DbUnionAllExpression that is being visited.
///
public override bool Visit(DbUnionAllExpression expression)
{
return VisitBinaryExpression(expression);
}
///
/// Returns false
///
/// The DbVariableReferenceExpression that is being visited.
/// false
public override bool Visit(DbVariableReferenceExpression expression)
{
return false;
}
#endregion
}
}