2015-04-07 09:35:12 +01:00
//---------------------------------------------------------------------
// <copyright file="BasicValidator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Diagnostics ;
using System.Data ;
using System.Data.Common ;
using System.Data.Metadata.Edm ;
namespace System.Data.Query.InternalTrees
{
#if DEBUG
/// <summary>
/// The BasicValidator validates the shape of the IQT. It ensures that the
/// various Ops in the tree have the right kinds and number of arguments.
/// </summary>
internal class BasicValidator : BasicOpVisitor
{
#region constructors
protected BasicValidator ( Command command )
{
m_command = command ;
}
#endregion
#region private surface
protected void Validate ( Node node )
{
VisitNode ( node ) ;
}
#region AssertHelpers
protected static void Assert ( bool condition , string format , int arg0 )
{
if ( ! condition )
{
Debug . Assert ( false , String . Format ( CultureInfo . InvariantCulture , format , arg0 ) ) ;
}
}
protected static void Assert ( bool condition , string format , OpType op )
{
if ( ! condition )
{
Debug . Assert ( false , String . Format ( CultureInfo . InvariantCulture , format , Dump . AutoString . ToString ( op ) ) ) ;
}
}
protected static void Assert ( bool condition , string format , OpType op , object arg1 )
{
if ( ! condition )
{
Debug . Assert ( false , String . Format ( CultureInfo . InvariantCulture , format , Dump . AutoString . ToString ( op ) , arg1 ) ) ;
}
}
protected static void Assert ( bool condition , string format , OpType op , object arg1 , object arg2 )
{
if ( ! condition )
{
Debug . Assert ( false , String . Format ( CultureInfo . InvariantCulture , format , Dump . AutoString . ToString ( op ) , arg1 , arg2 ) ) ;
}
}
protected static void Assert ( bool condition , string format , params object [ ] args )
{
if ( ! condition )
{
Debug . Assert ( false , String . Format ( CultureInfo . InvariantCulture , format , args ) ) ;
}
}
protected static void AssertArity ( Node n , int arity )
{
Assert ( arity = = n . Children . Count , "Op Arity mismatch for Op {0}: Expected {1} arguments; found {2} arguments" , n . Op . OpType , arity , n . Children . Count ) ;
}
protected static void AssertArity ( Node n )
{
if ( n . Op . Arity ! = Op . ArityVarying )
{
AssertArity ( n , n . Op . Arity ) ;
}
}
protected static void AssertBoolean ( TypeUsage type )
{
Assert ( TypeSemantics . IsPrimitiveType ( type , PrimitiveTypeKind . Boolean ) , "Type Mismatch: Expected Boolean; found {0} instead" , TypeHelpers . GetFullName ( type ) ) ;
}
protected static void AssertCollectionType ( TypeUsage type )
{
Assert ( TypeSemantics . IsCollectionType ( type ) , "Type Mismatch: Expected Collection type: Found {0}" , TypeHelpers . GetFullName ( type ) ) ;
}
protected static void AssertEqualTypes ( TypeUsage type1 , TypeUsage type2 )
{
Assert ( Command . EqualTypes ( type1 , type2 ) ,
"Type mismatch: " + type1 . Identity + ", " + type2 . Identity ) ;
}
protected static void AssertEqualTypes ( TypeUsage type1 , EdmType type2 )
{
AssertEqualTypes ( type1 , TypeUsage . Create ( type2 ) ) ;
}
protected static void AssertBooleanOp ( Op op )
{
AssertBoolean ( op . Type ) ;
}
protected static void AssertRelOp ( Op op )
{
Assert ( op . IsRelOp , "OpType Mismatch: Expected RelOp; found {0}" , op . OpType ) ;
}
protected static void AssertRelOpOrPhysicalOp ( Op op )
{
Assert ( op . IsRelOp | | op . IsPhysicalOp , "OpType Mismatch: Expected RelOp or PhysicalOp; found {0}" , op . OpType ) ;
}
protected static void AssertScalarOp ( Op op )
{
Assert ( op . IsScalarOp , "OpType Mismatch: Expected ScalarOp; found {0}" , op . OpType ) ;
}
protected static void AssertOpType ( Op op , OpType opType )
{
Assert ( op . OpType = = opType , "OpType Mismatch: Expected {0}; found {1}" , op . OpType , Dump . AutoString . ToString ( opType ) ) ;
}
protected static void AssertUnexpectedOp ( Op op )
{
Assert ( false , "Unexpected OpType {0}" , op . OpType ) ;
}
#endregion
#region Visitors
protected override void VisitDefault ( Node n )
{
Assert ( n . Id > = 0 , "Bad node id {0}" , n . Id ) ;
VisitChildren ( n ) ;
AssertArity ( n ) ;
}
#region ScalarOps
protected override void VisitScalarOpDefault ( ScalarOp op , Node n )
{
VisitDefault ( n ) ;
Assert ( op . Type ! = null , "ScalarOp {0} with no datatype!" , op . OpType ) ;
if ( op . OpType ! = OpType . Element & &
op . OpType ! = OpType . Exists & &
op . OpType ! = OpType . Collect )
{
foreach ( Node chi in n . Children )
{
AssertScalarOp ( chi . Op ) ;
}
}
}
public override void Visit ( AggregateOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
}
public override void Visit ( CaseOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
Assert ( ( n . Children . Count > = 3 & & n . Children . Count % 2 = = 1 ) ,
"CaseOp: Expected odd number of arguments, and at least 3; found {0}" , n . Children . Count ) ;
// Validate that each when statement is of type Boolean
for ( int i = 0 ; i < n . Children . Count - 1 ; i + = 2 )
{
Assert ( TypeSemantics . IsBooleanType ( n . Children [ i ] . Op . Type ) , "Encountered a when node with a non-boolean return type" ) ;
}
// Ensure that the then clauses, the else clause and the result type are all the same
for ( int i = 1 ; i < n . Children . Count - 1 ; i + = 2 )
{
AssertEqualTypes ( n . Op . Type , n . Children [ i ] . Op . Type ) ;
}
AssertEqualTypes ( n . Op . Type , n . Children [ n . Children . Count - 1 ] . Op . Type ) ;
}
public override void Visit ( ComparisonOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
AssertBooleanOp ( op ) ;
AssertEqualTypes ( n . Child0 . Op . Type , n . Child1 . Op . Type ) ;
}
public override void Visit ( ConditionalOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
switch ( op . OpType )
{
case OpType . And :
case OpType . Or :
AssertArity ( n , 2 ) ;
AssertBooleanOp ( n . Child0 . Op ) ;
AssertBooleanOp ( n . Child1 . Op ) ;
break ;
case OpType . Not :
AssertArity ( n , 1 ) ;
AssertBooleanOp ( n . Child0 . Op ) ;
break ;
case OpType . IsNull :
AssertArity ( n , 1 ) ;
break ;
default :
AssertUnexpectedOp ( op ) ;
break ;
}
AssertBooleanOp ( op ) ;
}
public override void Visit ( ArithmeticOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
switch ( op . OpType )
{
case OpType . Plus :
case OpType . Minus :
case OpType . Multiply :
case OpType . Divide :
case OpType . Modulo :
AssertEqualTypes ( n . Child0 . Op . Type , n . Child1 . Op . Type ) ;
AssertEqualTypes ( n . Op . Type , n . Child0 . Op . Type ) ;
AssertArity ( n , 2 ) ;
break ;
case OpType . UnaryMinus :
AssertArity ( n , 1 ) ;
break ;
default :
AssertUnexpectedOp ( op ) ;
break ;
}
}
public override void Visit ( ElementOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
AssertRelOp ( n . Child0 . Op ) ;
}
public override void Visit ( CollectOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
AssertOpType ( n . Child0 . Op , OpType . PhysicalProject ) ;
AssertCollectionType ( op . Type ) ;
}
public override void Visit ( DerefOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
Assert ( TypeSemantics . IsEntityType ( op . Type ) , "Expected an entity type. Found " + op . Type ) ;
Assert ( TypeSemantics . IsReferenceType ( n . Child0 . Op . Type ) , "Expected a ref type. Found " + n . Child0 . Op . Type ) ;
RefType r = n . Child0 . Op . Type . EdmType as RefType ;
Assert ( r . ElementType . EdmEquals ( op . Type . EdmType ) , "Inconsistent types" ) ;
}
public override void Visit ( ExistsOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
AssertRelOp ( n . Child0 . Op ) ;
AssertBooleanOp ( op ) ;
}
public override void Visit ( PropertyOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
AssertEqualTypes ( n . Child0 . Op . Type , op . PropertyInfo . DeclaringType ) ;
}
public override void Visit ( RelPropertyOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
Assert ( m_command . IsRelPropertyReferenced ( op . PropertyInfo ) , "no such rel property:" , op . PropertyInfo ) ;
Assert ( TypeSemantics . IsEntityType ( n . Child0 . Op . Type ) , "argument to RelPropertyOp must be an entity type. Found: " , n . Child0 . Op . Type ) ;
}
public override void Visit ( FunctionOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
Assert ( op . Function . Parameters . Count = = n . Children . Count , "FunctionOp: Argument count ({0}) does not match parameter count ({1})" , n . Children . Count , op . Function . Parameters . Count ) ;
for ( int idx = 0 ; idx < n . Children . Count ; idx + + )
{
AssertEqualTypes ( n . Children [ idx ] . Op . Type , op . Function . Parameters [ idx ] . TypeUsage ) ;
}
}
public override void Visit ( SoftCastOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
// [....] 9/21/06 - temporarily removing check here
// because the assert wrongly fails in some cases where the types are promotable,
// but the facets are not. Put this back when that issue is solved.
// Assert(TypeSemantics.IsEquivalentOrPromotableTo(n.Child0.Op.Type, op.Type), "Illegal SoftCastOp: Cannot promote input type {0} to target type {1}", n.Child0.Op.Type.Identity, op.Type.Identity);
}
public override void Visit ( NavigateOp op , Node n )
{
VisitScalarOpDefault ( op , n ) ;
}
#endregion
#region AncillaryOps
protected override void VisitAncillaryOpDefault ( AncillaryOp op , Node n )
{
VisitDefault ( n ) ;
}
public override void Visit ( VarDefOp op , Node n )
{
VisitAncillaryOpDefault ( op , n ) ;
AssertScalarOp ( n . Child0 . Op ) ;
VarDefOp varDefOp = ( VarDefOp ) op ;
AssertEqualTypes ( varDefOp . Var . Type , n . Child0 . Op . Type ) ;
}
public override void Visit ( VarDefListOp op , Node n )
{
VisitDefault ( n ) ;
foreach ( Node chi in n . Children )
{
AssertOpType ( chi . Op , OpType . VarDef ) ;
}
}
#endregion
#region RelOps
protected override void VisitRelOpDefault ( RelOp op , Node n )
{
VisitDefault ( n ) ;
}
protected override void VisitJoinOp ( JoinBaseOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
if ( op . OpType = = OpType . CrossJoin )
{
Assert ( n . Children . Count > = 2 , "CrossJoinOp needs at least 2 arguments; found only {0}" , n . Children . Count ) ;
return ;
}
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
AssertRelOpOrPhysicalOp ( n . Child1 . Op ) ;
AssertScalarOp ( n . Child2 . Op ) ;
AssertBooleanOp ( n . Child2 . Op ) ;
}
protected override void VisitApplyOp ( ApplyBaseOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
AssertRelOpOrPhysicalOp ( n . Child1 . Op ) ;
}
protected override void VisitSetOp ( SetOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
AssertRelOpOrPhysicalOp ( n . Child1 . Op ) ;
//
// Ensure that the corresponding setOp Vars are all of the same
// type
//
foreach ( VarMap varMap in op . VarMap )
{
foreach ( KeyValuePair < Var , Var > kv in varMap )
{
AssertEqualTypes ( kv . Key . Type , kv . Value . Type ) ;
}
}
}
protected override void VisitSortOp ( SortBaseOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
}
public override void Visit ( ConstrainedSortOp op , Node n )
{
base . Visit ( op , n ) ;
AssertScalarOp ( n . Child1 . Op ) ;
Assert ( TypeSemantics . IsIntegerNumericType ( n . Child1 . Op . Type ) , "ConstrainedSortOp Skip Count Node must have an integer result type" ) ;
AssertScalarOp ( n . Child2 . Op ) ;
Assert ( TypeSemantics . IsIntegerNumericType ( n . Child2 . Op . Type ) , "ConstrainedSortOp Limit Node must have an integer result type" ) ;
}
public override void Visit ( ScanTableOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
}
public override void Visit ( ScanViewOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOp ( n . Child0 . Op ) ;
}
public override void Visit ( FilterOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
AssertScalarOp ( n . Child1 . Op ) ;
AssertBooleanOp ( n . Child1 . Op ) ;
}
public override void Visit ( ProjectOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
AssertOpType ( n . Child1 . Op , OpType . VarDefList ) ;
}
public override void Visit ( UnnestOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertOpType ( n . Child0 . Op , OpType . VarDef ) ;
}
protected override void VisitGroupByOp ( GroupByBaseOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
for ( int i = 1 ; i < n . Children . Count ; i + + )
{
AssertOpType ( n . Children [ i ] . Op , OpType . VarDefList ) ;
}
}
public override void Visit ( GroupByIntoOp op , Node n )
{
VisitGroupByOp ( op , n ) ;
Assert ( n . Child3 . Children . Count > 0 , "GroupByInto with no group aggregate vars" ) ;
}
public override void Visit ( DistinctOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOp ( n . Child0 . Op ) ;
}
public override void Visit ( SingleRowTableOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
}
public override void Visit ( SingleRowOp op , Node n )
{
VisitRelOpDefault ( op , n ) ;
AssertRelOpOrPhysicalOp ( n . Child0 . Op ) ;
}
#endregion
#region PhysicalOps
protected override void VisitPhysicalOpDefault ( PhysicalOp op , Node n )
{
VisitDefault ( n ) ;
}
public override void Visit ( PhysicalProjectOp op , Node n )
{
VisitPhysicalOpDefault ( op , n ) ;
Assert ( n . Children . Count > = 1 , "PhysicalProjectOp needs at least 1 arg: found {0}" , n . Children . Count ) ;
foreach ( Node chi in n . Children )
{
AssertRelOpOrPhysicalOp ( chi . Op ) ;
}
}
public override void Visit ( SingleStreamNestOp op , Node n )
{
VisitPhysicalOpDefault ( op , n ) ;
AssertRelOp ( n . Child0 . Op ) ;
}
public override void Visit ( MultiStreamNestOp op , Node n )
{
VisitPhysicalOpDefault ( op , n ) ;
Assert ( n . Children . Count > 1 , "MultiStreamNestOp needs at least 2 arguments: found {0}" , n . Children . Count ) ;
foreach ( Node chi in n . Children )
{
AssertRelOpOrPhysicalOp ( chi . Op ) ;
}
}
#endregion
#endregion
#endregion
#region private state
private Command m_command ;
#endregion
}
#endif // DEBUG
}