File: SqlSelectClauseBuilder.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (185 lines) | stat: -rw-r--r-- 6,407 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//---------------------------------------------------------------------
// <copyright file="SqlSelectClauseBuilder.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace System.Data.SqlClient.SqlGen
{
    /// <summary>
    /// This class is used for building the SELECT clause of a Sql Statement
    /// It is used to gather information about required and optional columns
    /// and whether TOP and DISTINCT should be specified.
    /// 
    /// The underlying SqlBuilder is used for gathering the required columns.
    /// 
    /// The list of OptionalColumns is used for gathering the optional columns. 
    /// Whether a given OptionalColumn should be written is known only after the entire
    /// command tree has been processed. 
    /// 
    /// The IsDistinct property indicates that we want distinct columns.
    /// This is given out of band, since the input expression to the select clause
    /// may already have some columns projected out, and we use append-only SqlBuilders.
    /// The DISTINCT is inserted when we finally write the object into a string.
    /// 
    /// Also, we have a Top property, which is non-null if the number of results should
    /// be limited to certain number. It is given out of band for the same reasons as DISTINCT.
    ///
    /// </summary>
    internal class SqlSelectClauseBuilder : SqlBuilder
    {
        #region Fields and Properties
        private List<OptionalColumn> m_optionalColumns;
        internal void AddOptionalColumn(OptionalColumn column)
        {
            if (m_optionalColumns == null)
            {
                m_optionalColumns = new List<OptionalColumn>();
            }
            m_optionalColumns.Add(column);
        }

        private TopClause m_top;
        internal TopClause Top
        {
            get { return m_top; }
            set
            {
                Debug.Assert(m_top == null, "SqlSelectStatement.Top has already been set");
                m_top = value;
            }
        }

        /// <summary>
        /// Do we need to add a DISTINCT at the beginning of the SELECT
        /// </summary>
        internal bool IsDistinct
        {
            get;
            set;
        }

        /// <summary>
        /// Whether any columns have been specified.
        /// </summary>
        public override bool IsEmpty
        {
            get { return (base.IsEmpty) && (this.m_optionalColumns == null || this.m_optionalColumns.Count == 0); }
        }

        private readonly Func<bool> m_isPartOfTopMostStatement;
        #endregion

        #region Constructor
        internal SqlSelectClauseBuilder(Func<bool> isPartOfTopMostStatement)
        {
            this.m_isPartOfTopMostStatement = isPartOfTopMostStatement;
        }
        #endregion

        #region ISqlFragment Members

        /// <summary>
        /// Writes the string representing the Select statement:
        /// 
        /// SELECT (DISTINCT) (TOP topClause) (optionalColumns) (requiredColumns)
        /// 
        /// If Distinct is specified or this is part of a top most statement 
        /// all optional columns are marked as used.
        /// 
        /// Optional columns are only written if marked as used. 
        /// In addition, if no required columns are specified and no optional columns are 
        /// marked as used, the first optional column is written.
        /// 
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="sqlGenerator"></param>
        public override void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator)
        {
            writer.Write("SELECT ");
            if (IsDistinct)
            {
                writer.Write("DISTINCT ");
            }

            if (this.Top != null)
            {
                this.Top.WriteSql(writer, sqlGenerator);
            }

            if (this.IsEmpty)
            {
                Debug.Assert(false);  // we have removed all possibilities of SELECT *.
                writer.Write("*");
            }
            else
            {
                //Print the optional columns if any
                bool printedAny = WriteOptionalColumns(writer, sqlGenerator);

                if (!base.IsEmpty)
                {
                    if (printedAny)
                    {
                        writer.Write(", ");
                    }
                    base.WriteSql(writer, sqlGenerator);
                }
                //If no optional columns were printed and there were no other columns, 
                // print at least the first optional column
                else if (!printedAny)
                {
                    this.m_optionalColumns[0].MarkAsUsed();
                    m_optionalColumns[0].WriteSqlIfUsed(writer, sqlGenerator, "");
                }
            }
        }

        #endregion

        #region Private Helper Methods

        /// <summary>
        /// Writes the optional columns that are used. 
        /// If this is the topmost statement or distict is specifed as part of the same statement
        /// all optoinal columns are written.
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="sqlGenerator"></param>
        /// <returns>Whether at least one column got written</returns>
        private bool WriteOptionalColumns(SqlWriter writer, SqlGenerator sqlGenerator)
        {
            if (this.m_optionalColumns == null)
            {
                return false;
            }

            if (m_isPartOfTopMostStatement() || IsDistinct)
            {
                foreach (OptionalColumn column in this.m_optionalColumns)
                {
                    column.MarkAsUsed();
                }
            }

            string separator = "";
            bool printedAny = false;
            foreach (OptionalColumn column in this.m_optionalColumns)
            {
                if (column.WriteSqlIfUsed(writer, sqlGenerator, separator))
                {
                    printedAny = true;
                    separator = ", ";
                }
            }
            return printedAny;
        }
        #endregion
    }
}