//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Globalization; using md = System.Data.Metadata.Edm; using System.Data.Query.InternalTrees; using System.Data.Query.PlanCompiler; using System.Data.Query.ResultAssembly; using System.Diagnostics; using System.Text; namespace System.Data.Query.PlanCompiler { #if DEBUG /// /// The Validator class extends the BasicValidator and enforces that the ITree is valid /// through varying stages of the plan compilation process. At each stage, certain operators /// are illegal - and this validator is largely intended to tackle that /// internal class Validator : BasicValidator { #region public surface internal static void Validate(PlanCompiler compilerState, Node n) { Validator validator = new Validator(compilerState); validator.Validate(n); } internal static void Validate(PlanCompiler compilerState) { Validate(compilerState, compilerState.Command.Root); } #endregion #region constructors private Validator(PlanCompiler compilerState) : base(compilerState.Command) { m_compilerState = compilerState; } private static BitVec InitializeOpTypes() { BitVec validOpTypes = new BitVec(((int)OpType.MaxMarker + 1) * ((int)PlanCompilerPhase.MaxMarker + 1)); AddAllEntry(validOpTypes, OpType.Aggregate); AddAllEntry(validOpTypes, OpType.And); AddAllEntry(validOpTypes, OpType.Case); AddAllEntry(validOpTypes, OpType.Cast); AddEntry(validOpTypes, OpType.Collect, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE, PlanCompilerPhase.ProjectionPruning, PlanCompilerPhase.NestPullup); AddAllEntry(validOpTypes, OpType.Constant); AddAllEntry(validOpTypes, OpType.ConstantPredicate); AddAllEntry(validOpTypes, OpType.ConstrainedSort); AddAllEntry(validOpTypes, OpType.CrossApply); AddAllEntry(validOpTypes, OpType.CrossJoin); AddEntry(validOpTypes, OpType.Deref, PlanCompilerPhase.PreProcessor); AddAllEntry(validOpTypes, OpType.Distinct); AddAllEntry(validOpTypes, OpType.Divide); AddEntry(validOpTypes, OpType.Element, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.Transformations, PlanCompilerPhase.JoinElimination, PlanCompilerPhase.ProjectionPruning, PlanCompilerPhase.CodeGen, PlanCompilerPhase.PostCodeGen); AddAllEntry(validOpTypes, OpType.EQ); AddAllEntry(validOpTypes, OpType.Except); AddAllEntry(validOpTypes, OpType.Exists); AddAllEntry(validOpTypes, OpType.Filter); AddAllEntry(validOpTypes, OpType.FullOuterJoin); AddAllEntry(validOpTypes, OpType.Function); AddAllEntry(validOpTypes, OpType.GE); AddEntry(validOpTypes, OpType.GetEntityRef, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddEntry(validOpTypes, OpType.GetRefKey, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddAllEntry(validOpTypes, OpType.GroupBy); AddEntry(validOpTypes, OpType.GroupByInto, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE, PlanCompilerPhase.ProjectionPruning, PlanCompilerPhase.NestPullup); AddAllEntry(validOpTypes, OpType.GT); AddAllEntry(validOpTypes, OpType.InnerJoin); AddAllEntry(validOpTypes, OpType.InternalConstant); AddAllEntry(validOpTypes, OpType.Intersect); AddAllEntry(validOpTypes, OpType.IsNull); AddEntry(validOpTypes, OpType.IsOf, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddAllEntry(validOpTypes, OpType.LE); AddAllEntry(validOpTypes, OpType.LeftOuterJoin); AddAllEntry(validOpTypes, OpType.Like); AddAllEntry(validOpTypes, OpType.LT); AddAllEntry(validOpTypes, OpType.Minus); AddAllEntry(validOpTypes, OpType.Modulo); AddAllEntry(validOpTypes, OpType.Multiply); AddEntry(validOpTypes, OpType.Navigate, PlanCompilerPhase.PreProcessor); AddAllEntry(validOpTypes, OpType.NE); AddEntry(validOpTypes, OpType.NewEntity, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddEntry(validOpTypes, OpType.NewInstance, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddEntry(validOpTypes, OpType.DiscriminatedNewEntity, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddEntry(validOpTypes, OpType.NewMultiset, PlanCompilerPhase.PreProcessor); AddEntry(validOpTypes, OpType.NewRecord, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddAllEntry(validOpTypes, OpType.Not); AddAllEntry(validOpTypes, OpType.Null); AddAllEntry(validOpTypes, OpType.NullSentinel); AddAllEntry(validOpTypes, OpType.Or); AddAllEntry(validOpTypes, OpType.OuterApply); AddAllEntry(validOpTypes, OpType.PhysicalProject); AddAllEntry(validOpTypes, OpType.Plus); AddAllEntry(validOpTypes, OpType.Project); // Since, we don't support UDTs anymore - we shouldn't see PropertyOp after this AddEntry(validOpTypes, OpType.Property, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddEntry(validOpTypes, OpType.Ref, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddEntry(validOpTypes, OpType.RelProperty, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddAllEntry(validOpTypes, OpType.ScanTable); AddEntry(validOpTypes, OpType.ScanView, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddAllEntry(validOpTypes, OpType.SingleRow); AddAllEntry(validOpTypes, OpType.SingleRowTable); AddAllEntry(validOpTypes, OpType.SoftCast); AddAllEntry(validOpTypes, OpType.Sort); AddEntry(validOpTypes, OpType.Treat, PlanCompilerPhase.PreProcessor, PlanCompilerPhase.AggregatePushdown, PlanCompilerPhase.Normalization, PlanCompilerPhase.NTE); AddAllEntry(validOpTypes, OpType.UnaryMinus); AddAllEntry(validOpTypes, OpType.UnionAll); AddAllEntry(validOpTypes, OpType.Unnest); AddAllEntry(validOpTypes, OpType.VarDef); AddAllEntry(validOpTypes, OpType.VarDefList); AddAllEntry(validOpTypes, OpType.VarRef); return validOpTypes; } #endregion #region private methods #region Initializers private static int ComputeHash(OpType opType, PlanCompilerPhase phase) { int hash = ((int)opType * (int)PlanCompilerPhase.MaxMarker) + (int)phase; return hash; } private static void AddSingleEntry(BitVec opVector, OpType opType, PlanCompilerPhase phase) { int hash = ComputeHash(opType, phase); opVector.Set(hash); } private static void AddEntry(BitVec opVector, OpType opType, params PlanCompilerPhase[] phases) { foreach (PlanCompilerPhase phase in phases) { AddSingleEntry(opVector, opType, phase); } } private static void AddAllEntry(BitVec opVector, OpType opType) { foreach (PlanCompilerPhase phase in s_PlanCompilerPhases) { AddSingleEntry(opVector, opType, phase); } } private static bool CheckEntry(OpType opType, PlanCompilerPhase phase) { int hash = ComputeHash(opType, phase); return s_ValidOpTypes.IsSet(hash); } #endregion #region Visitors protected override void VisitDefault(Node n) { base.VisitDefault(n); Assert(CheckEntry(n.Op.OpType, m_compilerState.Phase), "Unxpected Op {0} in Phase {1}", n.Op.OpType, m_compilerState.Phase); } #region ScalarOps public override void Visit(NewEntityOp op, Node n) { base.Visit(op, n); if (m_compilerState.Phase > PlanCompilerPhase.PreProcessor && op.Type.EdmType.BuiltInTypeKind == md.BuiltInTypeKind.EntityType) { Assert(op.Scoped, "NewEntityOp for an entity type {0} is not scoped. All entity type constructors must be scoped after PreProcessor phase.", op.Type.EdmType.FullName); } } #endregion #region PhysicalOps #endregion #region RelOps #endregion #region AncillaryOps #endregion #endregion #endregion #region private state private PlanCompiler m_compilerState; private static PlanCompilerPhase[] s_PlanCompilerPhases = { PlanCompilerPhase.PreProcessor , PlanCompilerPhase.AggregatePushdown , PlanCompilerPhase.Normalization , PlanCompilerPhase.NTE , PlanCompilerPhase.ProjectionPruning , PlanCompilerPhase.NestPullup , PlanCompilerPhase.Transformations , PlanCompilerPhase.JoinElimination , PlanCompilerPhase.CodeGen , PlanCompilerPhase.PostCodeGen }; private static BitVec s_ValidOpTypes = InitializeOpTypes(); #endregion /// /// BitVector helper class; used to keep track of the used columns /// in the result assembly. /// /// /// BitVec can be a struct because it contains a readonly reference to an int[]. /// This code is a copy of System.Collections.BitArray so that we can have an efficient implementation of Minus. /// internal struct BitVec { private readonly int[] m_array; private readonly int m_length; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal BitVec(int length) { Debug.Assert(0 < length, "zero length"); m_array = new int[(length + 31) / 32]; m_length = length; } internal int Count { get { return m_length; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal void Set(int index) { Debug.Assert(unchecked((uint)index < (uint)m_length), "index out of range"); m_array[index / 32] |= (1 << (index % 32)); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal void ClearAll() { for (int i = 0; i < m_array.Length; i++) { m_array[i] = 0; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal bool IsEmpty() { for (int i = 0; i < m_array.Length; i++) { if (0 != m_array[i]) { return false; } } return true; } internal bool IsSet(int index) { Debug.Assert(unchecked((uint)index < (uint)m_length), "index out of range"); return (m_array[index / 32] & (1 << (index % 32))) != 0; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal void Or(BitVec value) { Debug.Assert(m_length == value.m_length, "unequal sized bitvec"); for (int i = 0; i < m_array.Length; i++) { m_array[i] |= value.m_array[i]; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal void Minus(BitVec value) { Debug.Assert(m_length == value.m_length, "unequal sized bitvec"); for (int i = 0; i < m_array.Length; i++) { m_array[i] &= ~value.m_array[i]; } } public override string ToString() { StringBuilder sb = new StringBuilder(3 * Count); string separator = string.Empty; for (int i = 0; i < Count; i++) { if (IsSet(i)) { sb.Append(separator); sb.Append(i); separator = ","; } } return sb.ToString(); } } } #endif // DEBUG }