//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @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 /// /// 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. /// 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 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 }