1 //---------------------------------------------------------------------
2 // <copyright file="Predicate.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
12 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
14 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
15 // to prevent from simple mistakes during development (e.g. method argument validation
16 // in cases where it was you who created the variables or the variables had already been validated or
17 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default
18 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are
19 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in
20 // the shipped product.
21 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions
22 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
23 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct
24 // or the tree was built/rewritten not the way we thought it was.
25 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
26 // PlanCompiler.Assert.
28 using System.Globalization;
30 using System.Data.Query.InternalTrees;
32 namespace System.Data.Query.PlanCompiler
35 /// The Predicate class represents a condition (predicate) in CNF.
36 /// A predicate consists of a number of "simple" parts, and the parts are considered to be
39 /// This class provides a number of useful functions related to
40 /// - Single Table predicates
42 /// - Key preservation
43 /// - Null preservation
46 /// Note: This class doesn't really convert node trees into CNF form. It looks for
47 /// basic CNF patterns, and reasons about them. For example,
49 /// can technically be translated into (a OR c) AND (b OR c),
50 /// but we don't bother.
51 /// At some future point of time, it might be appropriate to consider this
54 internal class Predicate
57 private Command m_command;
58 private List<Node> m_parts;
63 /// Create an empty predicate
65 /// <param name="command"></param>
66 internal Predicate(Command command)
69 m_parts = new List<Node>();
73 /// Create a predicate from a node tree
75 /// <param name="command">current iqt command</param>
76 /// <param name="andTree">the node tree</param>
77 internal Predicate(Command command, Node andTree)
80 PlanCompiler.Assert(andTree != null, "null node passed to Predicate() constructor");
81 InitFromAndTree(andTree);
85 #region public surface
87 #region construction APIs
89 /// Add a new "part" (simple predicate) to the current list of predicate parts
91 /// <param name="n">simple predicate</param>
92 internal void AddPart(Node n)
98 #region Reconstruction (of node tree)
100 /// Build up an AND tree based on the current parts.
101 /// Specifically, if I have parts (p1, p2, ..., pn), we build up a tree that looks like
102 /// p1 AND p2 AND ... AND pn
104 /// If we have no parts, we return a null reference
105 /// If we have only one part, then we return just that part
107 /// <returns>the and subtree</returns>
108 internal Node BuildAndTree()
111 foreach (Node n in m_parts)
119 andNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.And),
127 #region SingleTable (Filter) Predicates
130 /// Partition the current predicate into predicates that only apply
131 /// to the specified table (single-table-predicates), and others
133 /// <param name="tableDefinitions">current columns defined by the table</param>
134 /// <param name="otherPredicates">non-single-table predicates</param>
135 /// <returns>single-table-predicates</returns>
136 internal Predicate GetSingleTablePredicates(VarVec tableDefinitions,
137 out Predicate otherPredicates)
139 List<VarVec> tableDefinitionList = new List<VarVec>();
140 tableDefinitionList.Add(tableDefinitions);
141 List<Predicate> singleTablePredicateList;
142 GetSingleTablePredicates(tableDefinitionList, out singleTablePredicateList, out otherPredicates);
143 return singleTablePredicateList[0];
149 /// Get the set of equi-join columns from this predicate
151 /// <param name="leftTableDefinitions"></param>
152 /// <param name="rightTableDefinitions"></param>
153 /// <param name="leftTableEquiJoinColumns"></param>
154 /// <param name="rightTableEquiJoinColumns"></param>
155 /// <param name="otherPredicates"></param>
156 internal void GetEquiJoinPredicates(VarVec leftTableDefinitions, VarVec rightTableDefinitions,
157 out List<Var> leftTableEquiJoinColumns, out List<Var> rightTableEquiJoinColumns,
158 out Predicate otherPredicates)
160 otherPredicates = new Predicate(m_command);
161 leftTableEquiJoinColumns = new List<Var>();
162 rightTableEquiJoinColumns = new List<Var>();
163 foreach (Node part in m_parts)
168 if (IsEquiJoinPredicate(part, leftTableDefinitions, rightTableDefinitions, out leftTableVar, out rightTableVar))
170 leftTableEquiJoinColumns.Add(leftTableVar);
171 rightTableEquiJoinColumns.Add(rightTableVar);
175 otherPredicates.AddPart(part);
180 internal Predicate GetJoinPredicates(VarVec leftTableDefinitions, VarVec rightTableDefinitions,
181 out Predicate otherPredicates)
183 Predicate joinPredicate = new Predicate(m_command);
184 otherPredicates = new Predicate(m_command);
186 foreach (Node part in m_parts)
191 if (Predicate.IsEquiJoinPredicate(part, leftTableDefinitions, rightTableDefinitions, out leftTableVar, out rightTableVar))
193 joinPredicate.AddPart(part);
197 otherPredicates.AddPart(part);
200 return joinPredicate;
206 /// Is the current predicate a "key-satisfying" predicate?
208 /// <param name="keyVars">list of keyVars</param>
209 /// <param name="definitions">current table definitions</param>
210 /// <returns>true, if this predicate satisfies the keys</returns>
211 internal bool SatisfiesKey(VarVec keyVars, VarVec definitions)
213 if (keyVars.Count > 0)
215 VarVec missingKeys = keyVars.Clone();
216 foreach (Node part in m_parts)
218 if (part.Op.OpType != OpType.EQ)
223 if (IsKeyPredicate(part.Child0, part.Child1, keyVars, definitions, out keyVar))
225 missingKeys.Clear(keyVar);
227 else if (IsKeyPredicate(part.Child1, part.Child0, keyVars, definitions, out keyVar))
229 missingKeys.Clear(keyVar);
233 return missingKeys.IsEmpty;
241 /// Does this predicate preserve nulls for the table columns?
243 /// If the ansiNullSemantics parameter is set, then we simply return true
244 /// always - this shuts off most optimizations
247 /// <param name="tableColumns">list of columns to consider</param>
248 /// <param name="ansiNullSemantics">use ansi null semantics</param>
249 /// <returns>true, if the predicate preserves nulls</returns>
250 internal bool PreservesNulls(VarVec tableColumns, bool ansiNullSemantics)
252 // Don't mess with non-ansi semantics
253 if (!ansiNullSemantics)
258 // If at least one part does not preserve nulls, then we simply return false
259 foreach (Node part in m_parts)
261 if (!PreservesNulls(part, tableColumns))
272 #region private methods
274 private void InitFromAndTree(Node andTree)
276 if (andTree.Op.OpType == OpType.And)
278 InitFromAndTree(andTree.Child0);
279 InitFromAndTree(andTree.Child1);
283 m_parts.Add(andTree);
288 #region Single Table Predicates
290 private void GetSingleTablePredicates(List<VarVec> tableDefinitions,
291 out List<Predicate> singleTablePredicates, out Predicate otherPredicates)
293 singleTablePredicates = new List<Predicate>();
294 foreach (VarVec vec in tableDefinitions)
296 singleTablePredicates.Add(new Predicate(m_command));
298 otherPredicates = new Predicate(m_command);
299 VarVec externalRefs = m_command.CreateVarVec();
301 foreach (Node part in m_parts)
303 NodeInfo nodeInfo = m_command.GetNodeInfo(part);
305 bool singleTablePart = false;
306 for (int i = 0; i < tableDefinitions.Count; i++)
308 VarVec tableColumns = tableDefinitions[i];
309 if (tableColumns != null)
311 externalRefs.InitFrom(nodeInfo.ExternalReferences);
312 externalRefs.Minus(tableColumns);
313 if (externalRefs.IsEmpty)
315 singleTablePart = true;
316 singleTablePredicates[i].AddPart(part);
321 if (!singleTablePart)
323 otherPredicates.AddPart(part);
332 /// Is this "simple" predicate an equi-join predicate?
333 /// (ie) is it of the form "var1 = var2"
334 /// Return "var1" and "var2"
336 /// <param name="simplePredicateNode">the simple predicate</param>
337 /// <param name="leftVar">var on the left-side</param>
338 /// <param name="rightVar">var on the right</param>
339 /// <returns>true, if this is an equijoin predicate</returns>
340 private static bool IsEquiJoinPredicate(Node simplePredicateNode, out Var leftVar, out Var rightVar)
344 if (simplePredicateNode.Op.OpType != OpType.EQ)
349 VarRefOp leftVarOp = simplePredicateNode.Child0.Op as VarRefOp;
350 if (leftVarOp == null)
354 VarRefOp rightVarOp = simplePredicateNode.Child1.Op as VarRefOp;
355 if (rightVarOp == null)
360 leftVar = leftVarOp.Var;
361 rightVar = rightVarOp.Var;
366 /// Is this an equi-join predicate involving columns from the specified tables?
367 /// On output, if this was indeed an equijoin predicate, "leftVar" is the
368 /// column of the left table, while "rightVar" is the column of the right table
369 /// and the predicate itself is of the form "leftVar = rightVar"
371 /// <param name="simplePredicateNode">the simple predicate node</param>
372 /// <param name="leftTableDefinitions">interesting columns of the left table</param>
373 /// <param name="rightTableDefinitions">interesting columns of the right table</param>
374 /// <param name="leftVar">join column of the left table</param>
375 /// <param name="rightVar">join column of the right table</param>
376 /// <returns>true, if this is an equijoin predicate involving columns from the 2 tables</returns>
377 private static bool IsEquiJoinPredicate(Node simplePredicateNode,
378 VarVec leftTableDefinitions, VarVec rightTableDefinitions,
379 out Var leftVar, out Var rightVar)
386 if (!Predicate.IsEquiJoinPredicate(simplePredicateNode, out tempLeftVar, out tempRightVar))
391 if (leftTableDefinitions.IsSet(tempLeftVar) &&
392 rightTableDefinitions.IsSet(tempRightVar))
394 leftVar = tempLeftVar;
395 rightVar = tempRightVar;
397 else if (leftTableDefinitions.IsSet(tempRightVar) &&
398 rightTableDefinitions.IsSet(tempLeftVar))
400 leftVar = tempRightVar;
401 rightVar = tempLeftVar;
414 /// Does this predicate preserve nulls on the specified columns of the table?
415 /// If any of the columns participates in a comparison predicate, or in a
416 /// not-null predicate, then, nulls are not preserved
418 /// <param name="simplePredNode">the "simple" predicate node</param>
419 /// <param name="tableColumns">list of table columns</param>
420 /// <returns>true, if nulls are preserved</returns>
421 private static bool PreservesNulls(Node simplePredNode, VarVec tableColumns)
425 switch (simplePredNode.Op.OpType)
433 varRefOp = simplePredNode.Child0.Op as VarRefOp;
434 if (varRefOp != null && tableColumns.IsSet(varRefOp.Var))
438 varRefOp = simplePredNode.Child1.Op as VarRefOp;
439 if (varRefOp != null && tableColumns.IsSet(varRefOp.Var))
446 if (simplePredNode.Child0.Op.OpType != OpType.IsNull)
450 varRefOp = simplePredNode.Child0.Child0.Op as VarRefOp;
451 return (varRefOp == null || !tableColumns.IsSet(varRefOp.Var));
454 // If the predicate is "column LIKE constant ...", then the
455 // predicate does not preserve nulls
456 ConstantBaseOp constantOp = simplePredNode.Child1.Op as ConstantBaseOp;
457 if (constantOp == null || (constantOp.OpType == OpType.Null))
461 varRefOp = simplePredNode.Child0.Op as VarRefOp;
462 if (varRefOp != null && tableColumns.IsSet(varRefOp.Var))
475 private bool IsKeyPredicate(Node left, Node right, VarVec keyVars, VarVec definitions, out Var keyVar)
479 // If the left-side is not a Var, then return false
480 if (left.Op.OpType != OpType.VarRef)
484 VarRefOp varRefOp = (VarRefOp)left.Op;
485 keyVar = varRefOp.Var;
487 // Not a key of this table?
488 if (!keyVars.IsSet(keyVar))
493 // Make sure that the other side is either a constant, or has no
494 // references at all to us
495 NodeInfo otherNodeInfo = m_command.GetNodeInfo(right);
496 VarVec otherVarExternalReferences = otherNodeInfo.ExternalReferences.Clone();
497 otherVarExternalReferences.And(definitions);
498 return otherVarExternalReferences.IsEmpty;