Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / SqlClient / SqlGen / SqlSelectClauseBuilder.cs
1 //---------------------------------------------------------------------
2 // <copyright file="SqlSelectClauseBuilder.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  [....]
7 //---------------------------------------------------------------------
8
9 using System;
10 using System.Collections.Generic;
11 using System.Diagnostics;
12
13 namespace System.Data.SqlClient.SqlGen
14 {
15     /// <summary>
16     /// This class is used for building the SELECT clause of a Sql Statement
17     /// It is used to gather information about required and optional columns
18     /// and whether TOP and DISTINCT should be specified.
19     /// 
20     /// The underlying SqlBuilder is used for gathering the required columns.
21     /// 
22     /// The list of OptionalColumns is used for gathering the optional columns. 
23     /// Whether a given OptionalColumn should be written is known only after the entire
24     /// command tree has been processed. 
25     /// 
26     /// The IsDistinct property indicates that we want distinct columns.
27     /// This is given out of band, since the input expression to the select clause
28     /// may already have some columns projected out, and we use append-only SqlBuilders.
29     /// The DISTINCT is inserted when we finally write the object into a string.
30     /// 
31     /// Also, we have a Top property, which is non-null if the number of results should
32     /// be limited to certain number. It is given out of band for the same reasons as DISTINCT.
33     ///
34     /// </summary>
35     internal class SqlSelectClauseBuilder : SqlBuilder
36     {
37         #region Fields and Properties
38         private List<OptionalColumn> m_optionalColumns;
39         internal void AddOptionalColumn(OptionalColumn column)
40         {
41             if (m_optionalColumns == null)
42             {
43                 m_optionalColumns = new List<OptionalColumn>();
44             }
45             m_optionalColumns.Add(column);
46         }
47
48         private TopClause m_top;
49         internal TopClause Top
50         {
51             get { return m_top; }
52             set
53             {
54                 Debug.Assert(m_top == null, "SqlSelectStatement.Top has already been set");
55                 m_top = value;
56             }
57         }
58
59         /// <summary>
60         /// Do we need to add a DISTINCT at the beginning of the SELECT
61         /// </summary>
62         internal bool IsDistinct
63         {
64             get;
65             set;
66         }
67
68         /// <summary>
69         /// Whether any columns have been specified.
70         /// </summary>
71         public override bool IsEmpty
72         {
73             get { return (base.IsEmpty) && (this.m_optionalColumns == null || this.m_optionalColumns.Count == 0); }
74         }
75
76         private readonly Func<bool> m_isPartOfTopMostStatement;
77         #endregion
78
79         #region Constructor
80         internal SqlSelectClauseBuilder(Func<bool> isPartOfTopMostStatement)
81         {
82             this.m_isPartOfTopMostStatement = isPartOfTopMostStatement;
83         }
84         #endregion
85
86         #region ISqlFragment Members
87
88         /// <summary>
89         /// Writes the string representing the Select statement:
90         /// 
91         /// SELECT (DISTINCT) (TOP topClause) (optionalColumns) (requiredColumns)
92         /// 
93         /// If Distinct is specified or this is part of a top most statement 
94         /// all optional columns are marked as used.
95         /// 
96         /// Optional columns are only written if marked as used. 
97         /// In addition, if no required columns are specified and no optional columns are 
98         /// marked as used, the first optional column is written.
99         /// 
100         /// </summary>
101         /// <param name="writer"></param>
102         /// <param name="sqlGenerator"></param>
103         public override void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator)
104         {
105             writer.Write("SELECT ");
106             if (IsDistinct)
107             {
108                 writer.Write("DISTINCT ");
109             }
110
111             if (this.Top != null)
112             {
113                 this.Top.WriteSql(writer, sqlGenerator);
114             }
115
116             if (this.IsEmpty)
117             {
118                 Debug.Assert(false);  // we have removed all possibilities of SELECT *.
119                 writer.Write("*");
120             }
121             else
122             {
123                 //Print the optional columns if any
124                 bool printedAny = WriteOptionalColumns(writer, sqlGenerator);
125
126                 if (!base.IsEmpty)
127                 {
128                     if (printedAny)
129                     {
130                         writer.Write(", ");
131                     }
132                     base.WriteSql(writer, sqlGenerator);
133                 }
134                 //If no optional columns were printed and there were no other columns, 
135                 // print at least the first optional column
136                 else if (!printedAny)
137                 {
138                     this.m_optionalColumns[0].MarkAsUsed();
139                     m_optionalColumns[0].WriteSqlIfUsed(writer, sqlGenerator, "");
140                 }
141             }
142         }
143
144         #endregion
145
146         #region Private Helper Methods
147
148         /// <summary>
149         /// Writes the optional columns that are used. 
150         /// If this is the topmost statement or distict is specifed as part of the same statement
151         /// all optoinal columns are written.
152         /// </summary>
153         /// <param name="writer"></param>
154         /// <param name="sqlGenerator"></param>
155         /// <returns>Whether at least one column got written</returns>
156         private bool WriteOptionalColumns(SqlWriter writer, SqlGenerator sqlGenerator)
157         {
158             if (this.m_optionalColumns == null)
159             {
160                 return false;
161             }
162
163             if (m_isPartOfTopMostStatement() || IsDistinct)
164             {
165                 foreach (OptionalColumn column in this.m_optionalColumns)
166                 {
167                     column.MarkAsUsed();
168                 }
169             }
170
171             string separator = "";
172             bool printedAny = false;
173             foreach (OptionalColumn column in this.m_optionalColumns)
174             {
175                 if (column.WriteSqlIfUsed(writer, sqlGenerator, separator))
176                 {
177                     printedAny = true;
178                     separator = ", ";
179                 }
180             }
181             return printedAny;
182         }
183         #endregion
184     }
185 }