| 
									
										
										
										
											2016-08-03 10:59:49 +00:00
										 |  |  | //--------------------------------------------------------------------- | 
					
						
							|  |  |  | // <copyright file="SqlSelectStatement.cs" company="Microsoft"> | 
					
						
							|  |  |  | //      Copyright (c) Microsoft Corporation.  All rights reserved. | 
					
						
							|  |  |  | // </copyright> | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2017-08-21 15:34:15 +00:00
										 |  |  | // @owner  Microsoft | 
					
						
							|  |  |  | // @backupOwner Microsoft | 
					
						
							| 
									
										
										
										
											2016-08-03 10:59:49 +00:00
										 |  |  | //--------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using System; | 
					
						
							|  |  |  | using System.Collections.Generic; | 
					
						
							|  |  |  | using System.Diagnostics; | 
					
						
							|  |  |  | using System.Data.Common.CommandTrees; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace System.Data.SqlClient.SqlGen | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /// <summary> | 
					
						
							|  |  |  |     /// A SqlSelectStatement represents a canonical SQL SELECT statement. | 
					
						
							|  |  |  |     /// It has fields for the 5 main clauses | 
					
						
							|  |  |  |     /// <list type="number"> | 
					
						
							|  |  |  |     /// <item>SELECT</item> | 
					
						
							|  |  |  |     /// <item>FROM</item> | 
					
						
							|  |  |  |     /// <item>WHERE</item> | 
					
						
							|  |  |  |     /// <item>GROUP BY</item> | 
					
						
							|  |  |  |     /// <item>ORDER BY</item> | 
					
						
							|  |  |  |     /// </list> | 
					
						
							|  |  |  |     /// We do not have HAVING, since the CQT does not have such a node. | 
					
						
							|  |  |  |     /// Each of the fields is a SqlBuilder, so we can keep appending SQL strings | 
					
						
							|  |  |  |     /// or other fragments to build up the clause. | 
					
						
							|  |  |  |     /// | 
					
						
							|  |  |  |     /// The FromExtents contains the list of inputs in use for the select statement. | 
					
						
							|  |  |  |     /// There is usually just one element in this - Select statements for joins may | 
					
						
							|  |  |  |     /// temporarily have more than one. | 
					
						
							|  |  |  |     /// | 
					
						
							|  |  |  |     /// If the select statement is created by a Join node, we maintain a list of | 
					
						
							|  |  |  |     /// all the extents that have been flattened in the join in AllJoinExtents | 
					
						
							|  |  |  |     /// <example> | 
					
						
							|  |  |  |     /// in J(j1= J(a,b), c) | 
					
						
							|  |  |  |     /// FromExtents has 2 nodes JoinSymbol(name=j1, ...) and Symbol(name=c) | 
					
						
							|  |  |  |     /// AllJoinExtents has 3 nodes Symbol(name=a), Symbol(name=b), Symbol(name=c) | 
					
						
							|  |  |  |     /// </example> | 
					
						
							|  |  |  |     /// | 
					
						
							|  |  |  |     /// If any expression in the non-FROM clause refers to an extent in a higher scope, | 
					
						
							|  |  |  |     /// we add that extent to the OuterExtents list.  This list denotes the list | 
					
						
							|  |  |  |     /// of extent aliases that may collide with the aliases used in this select statement. | 
					
						
							|  |  |  |     /// It is set by <see cref="SqlGenerator.Visit(DbVariableReferenceExpression)"/>. | 
					
						
							|  |  |  |     /// An extent is an outer extent if it is not one of the FromExtents. | 
					
						
							|  |  |  |     /// | 
					
						
							|  |  |  |     /// | 
					
						
							|  |  |  |     /// </summary> | 
					
						
							|  |  |  |     internal sealed class SqlSelectStatement : ISqlFragment | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Whether the columns ouput by this sql statement were renamed from what given in the command tree. | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         internal bool OutputColumnsRenamed | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get; | 
					
						
							|  |  |  |             set; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// A dictionary of output columns | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         internal Dictionary<string, Symbol> OutputColumns | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get; | 
					
						
							|  |  |  |             set; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         internal List<Symbol> AllJoinExtents | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get; | 
					
						
							|  |  |  |             // We have a setter as well, even though this is a list, | 
					
						
							|  |  |  |             // since we use this field only in special cases. | 
					
						
							|  |  |  |             set; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private List<Symbol> fromExtents; | 
					
						
							|  |  |  |         internal List<Symbol> FromExtents | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (null == fromExtents) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     fromExtents = new List<Symbol>(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return fromExtents; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private Dictionary<Symbol, bool> outerExtents; | 
					
						
							|  |  |  |         internal Dictionary<Symbol, bool> OuterExtents | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (null == outerExtents) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     outerExtents = new Dictionary<Symbol, bool>(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return outerExtents; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private readonly SqlSelectClauseBuilder select; | 
					
						
							|  |  |  |         internal SqlSelectClauseBuilder Select | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get { return select; } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private readonly SqlBuilder from = new SqlBuilder(); | 
					
						
							|  |  |  |         internal SqlBuilder From | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get { return from; } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private SqlBuilder where; | 
					
						
							|  |  |  |         internal SqlBuilder Where | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (null == where) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     where = new SqlBuilder(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return where; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private SqlBuilder groupBy; | 
					
						
							|  |  |  |         internal SqlBuilder GroupBy | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (null == groupBy) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     groupBy = new SqlBuilder(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return groupBy; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private SqlBuilder orderBy; | 
					
						
							|  |  |  |         public SqlBuilder OrderBy | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (null == orderBy) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     orderBy = new SqlBuilder(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return orderBy; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //indicates whether it is the top most select statement,  | 
					
						
							|  |  |  |         // if not Order By should be omitted unless there is a corresponding TOP | 
					
						
							|  |  |  |         internal bool IsTopMost | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             get; | 
					
						
							|  |  |  |             set; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         #region Internal Constructor | 
					
						
							|  |  |  |         internal SqlSelectStatement() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             this.select = new SqlSelectClauseBuilder(delegate() { return this.IsTopMost; }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         #endregion | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         #region ISqlFragment Members | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Write out a SQL select statement as a string. | 
					
						
							|  |  |  |         /// We have to | 
					
						
							|  |  |  |         /// <list type="number"> | 
					
						
							|  |  |  |         /// <item>Check whether the aliases extents we use in this statement have | 
					
						
							|  |  |  |         /// to be renamed. | 
					
						
							|  |  |  |         /// We first create a list of all the aliases used by the outer extents. | 
					
						
							|  |  |  |         /// For each of the FromExtents( or AllJoinExtents if it is non-null), | 
					
						
							|  |  |  |         /// rename it if it collides with the previous list. | 
					
						
							|  |  |  |         /// </item> | 
					
						
							|  |  |  |         /// <item>Write each of the clauses (if it exists) as a string</item> | 
					
						
							|  |  |  |         /// </list> | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="writer"></param> | 
					
						
							|  |  |  |         /// <param name="sqlGenerator"></param> | 
					
						
							|  |  |  |         public void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             #region Check if FROM aliases need to be renamed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Create a list of the aliases used by the outer extents | 
					
						
							|  |  |  |             // JoinSymbols have to be treated specially. | 
					
						
							|  |  |  |             List<string> outerExtentAliases = null; | 
					
						
							|  |  |  |             if ((null != outerExtents) && (0 < outerExtents.Count)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 foreach (Symbol outerExtent in outerExtents.Keys) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     JoinSymbol joinSymbol = outerExtent as JoinSymbol; | 
					
						
							|  |  |  |                     if (joinSymbol != null) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         foreach (Symbol symbol in joinSymbol.FlattenedExtentList) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             if (null == outerExtentAliases) { outerExtentAliases = new List<string>(); } | 
					
						
							|  |  |  |                             outerExtentAliases.Add(symbol.NewName); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     else | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         if (null == outerExtentAliases) { outerExtentAliases = new List<string>(); } | 
					
						
							|  |  |  |                         outerExtentAliases.Add(outerExtent.NewName); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }         | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // An then rename each of the FromExtents we have | 
					
						
							|  |  |  |             // If AllJoinExtents is non-null - it has precedence. | 
					
						
							|  |  |  |             // The new name is derived from the old name - we append an increasing int. | 
					
						
							|  |  |  |             List<Symbol> extentList = this.AllJoinExtents ?? this.fromExtents; | 
					
						
							|  |  |  |             if (null != extentList) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 foreach (Symbol fromAlias in extentList) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     if ((null != outerExtentAliases) && outerExtentAliases.Contains(fromAlias.Name)) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         int i = sqlGenerator.AllExtentNames[fromAlias.Name]; | 
					
						
							|  |  |  |                         string newName; | 
					
						
							|  |  |  |                         do | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             ++i; | 
					
						
							|  |  |  |                             newName = fromAlias.Name + i.ToString(System.Globalization.CultureInfo.InvariantCulture); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         while (sqlGenerator.AllExtentNames.ContainsKey(newName)); | 
					
						
							|  |  |  |                         sqlGenerator.AllExtentNames[fromAlias.Name] = i; | 
					
						
							|  |  |  |                         fromAlias.NewName = newName; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         // Add extent to list of known names (although i is always incrementing, "prefix11" can | 
					
						
							|  |  |  |                         // eventually collide with "prefix1" when it is extended) | 
					
						
							|  |  |  |                         sqlGenerator.AllExtentNames[newName] = 0; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // Add the current alias to the list, so that the extents | 
					
						
							|  |  |  |                     // that follow do not collide with me. | 
					
						
							|  |  |  |                     if (null == outerExtentAliases) { outerExtentAliases = new List<string>(); } | 
					
						
							|  |  |  |                     outerExtentAliases.Add(fromAlias.NewName); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             #endregion | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Increase the indent, so that the Sql statement is nested by one tab. | 
					
						
							|  |  |  |             writer.Indent += 1; // ++ can be confusing in this context | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             this.select.WriteSql(writer, sqlGenerator); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             writer.WriteLine(); | 
					
						
							|  |  |  |             writer.Write("FROM "); | 
					
						
							|  |  |  |             this.From.WriteSql(writer, sqlGenerator); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ((null != this.where) && !this.Where.IsEmpty) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 writer.WriteLine(); | 
					
						
							|  |  |  |                 writer.Write("WHERE "); | 
					
						
							|  |  |  |                 this.Where.WriteSql(writer, sqlGenerator); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ((null != this.groupBy) && !this.GroupBy.IsEmpty) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 writer.WriteLine(); | 
					
						
							|  |  |  |                 writer.Write("GROUP BY "); | 
					
						
							|  |  |  |                 this.GroupBy.WriteSql(writer, sqlGenerator); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ((null != this.orderBy) && !this.OrderBy.IsEmpty && (this.IsTopMost || this.Select.Top != null)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 writer.WriteLine(); | 
					
						
							|  |  |  |                 writer.Write("ORDER BY "); | 
					
						
							|  |  |  |                 this.OrderBy.WriteSql(writer, sqlGenerator); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             --writer.Indent; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         #endregion | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |