// Copyright © 2008, 2014, Oracle and/or its affiliates. All rights reserved. // // MySQL Connector/NET is licensed under the terms of the GPLv2 // , like most // MySQL Connectors. There are special exceptions to the terms and // conditions of the GPLv2 as it is applied to this software, see the // FLOSS License Exception // . // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA using System.Collections.Generic; using System.Text; using System; using System.Collections; using System.Diagnostics; #if EF6 using System.Data.Entity.Core.Common.CommandTrees; using System.Data.Entity.Core.Metadata.Edm; #else using System.Data.Metadata.Edm; using System.Data.Common.CommandTrees; #endif namespace MySql.Data.Entity { class SelectStatement : InputFragment { private Dictionary columnHash; private bool hasRenamedColumns; private SqlGenerator generator; public SelectStatement(SqlGenerator generator) : base(null) { Columns = new List(); this.generator = generator; } public InputFragment From; public List Columns { get; private set; } public SqlFragment Where; public SqlFragment Limit; public SqlFragment Skip; public List GroupBy { get; internal set; } public List OrderBy { get; internal set; } public bool IsDistinct; public void AddGroupBy(SqlFragment f) { if (GroupBy == null) GroupBy = new List(); GroupBy.Add(f); } public void AddOrderBy(SortFragment f) { if (OrderBy == null) OrderBy = new List(); OrderBy.Add(f); } public override void WriteSql(StringBuilder sql) { if (IsWrapped) sql.Append("("); sql.Append("SELECT"); if (IsDistinct) sql.Append(" DISTINCT "); WriteList(Columns, sql); if (From != null) { sql.Append("\r\nFROM "); From.WriteSql(sql); } if (Where != null) { sql.Append("\r\n WHERE "); Where.WriteSql(sql); } if (GroupBy != null) { sql.Append("\r\n GROUP BY "); WriteList(GroupBy, sql); } WriteOrderBy(sql); if (Limit != null || Skip != null) { sql.Append(" LIMIT "); if (Skip != null) sql.AppendFormat("{0},", Skip); if (Limit == null) sql.Append("18446744073709551615"); else sql.AppendFormat("{0}", Limit); } if (IsWrapped) { sql.Append(")"); if (Name != null) sql.AppendFormat(" AS {0}", QuoteIdentifier(Name)); } } private void WriteOrderBy(StringBuilder sql) { if (OrderBy == null) return; sql.Append("\r\n ORDER BY "); WriteList(OrderBy, sql); } public override void Wrap(Scope scope) { base.Wrap(scope); // now we need to add default columns if necessary if (Columns.Count == 0) AddDefaultColumns(scope); // next we need to remove child extents of the select from scope if (Name != null) { scope.Remove(this); scope.Add(Name, this); } } void AddDefaultColumns(Scope scope) { if (columnHash == null) columnHash = new Dictionary(); List columns = GetDefaultColumnsForFragment(From); bool Exists = false; if (From is TableFragment && scope.GetFragment((From as TableFragment).Table) == null) { scope.Add((From as TableFragment).Table, From); Exists = true; } foreach (ColumnFragment column in columns) { // first we need to set the input for this column InputFragment input = scope.FindInputFromProperties(column.PropertyFragment); column.TableName = input.Name; // then we rename the column if necessary if (columnHash.ContainsKey(column.ColumnName.ToUpper())) { column.ColumnAlias = MakeColumnNameUnique(column.ColumnName); columnHash.Add(column.ColumnAlias, column); } else columnHash.Add(column.ColumnName.ToUpper(), column); Columns.Add(column); } if (Exists) { scope.Remove((From as TableFragment).Table, From); } } internal void AddColumn(ColumnFragment column, Scope scope) { InputFragment input = scope.FindInputFromProperties(column.PropertyFragment); column.TableName = input.Name; // then we rename the column if necessary if (columnHash.ContainsKey(column.ColumnName.ToUpper())) { column.ColumnAlias = MakeColumnNameUnique(column.ColumnName); columnHash.Add(column.ColumnAlias, column); } else { if( !string.IsNullOrEmpty(column.ColumnAlias) ) columnHash.Add(column.ColumnAlias.ToUpper(), column); else columnHash.Add(column.ColumnName.ToUpper(), column); } Columns.Add(column); } List GetDefaultColumnsForFragment(InputFragment input) { List columns = new List(); if (input is TableFragment) { return GetDefaultColumnsForTable(input as TableFragment); } else if (input is JoinFragment || input is UnionFragment) { Debug.Assert(input.Left != null); if (input is UnionFragment) { generator.Ops.Push(OpType.Union); } columns = GetDefaultColumnsForFragment(input.Left); if (input is JoinFragment && input.Right != null) { List right = GetDefaultColumnsForFragment(input.Right); columns.AddRange(right); } if (input is UnionFragment) { generator.Ops.Pop(); } } else if (input is SelectStatement) { SelectStatement select = input as SelectStatement; foreach (ColumnFragment cf in select.Columns) { ColumnFragment newColumn = new ColumnFragment(cf.TableName, string.IsNullOrEmpty(cf.ColumnAlias) ? cf.ActualColumnName : cf.ColumnAlias ); if ( generator.GetTopOp() == OpType.Join ) { newColumn.ColumnAlias = cf.ColumnAlias; newColumn.PushInput(cf.ColumnName); if (cf.TableName != null) newColumn.PushInput(cf.TableName); } else { newColumn.PushInput(cf.ActualColumnName); if (cf.TableName != null && cf.ColumnAlias == null) newColumn.PushInput(cf.TableName); } if (select.Name != null) { newColumn.PushInput(select.Name); // add the scope } columns.Add(newColumn); } return columns; } else throw new NotImplementedException(); if (!String.IsNullOrEmpty(input.Name) && input.Name != From.Name) foreach (ColumnFragment c in columns) { c.PushInput(input.Name); } return columns; } List GetDefaultColumnsForTable(TableFragment table) { List columns = new List(); foreach (EdmProperty property in Metadata.GetProperties(table.Type.EdmType)) { ColumnFragment col = new ColumnFragment(table.Name, property.Name); col.PushInput(property.Name); col.PushInput((table.Name != null) ? table.Name : table.Table); columns.Add(col); } return columns; } private string MakeColumnNameUnique(string baseName) { int i = 1; baseName = baseName.ToUpper(); hasRenamedColumns = true; while (true) { string name = String.Format("{0}{1}", baseName, i); if (!columnHash.ContainsKey(name)) return name; i++; } } public bool HasDifferentNameForColumn(ColumnFragment column) { if (!hasRenamedColumns) return false; foreach (ColumnFragment c in Columns) { if (!c.Equals(column)) continue; if (String.IsNullOrEmpty(c.ColumnAlias)) return false; column.ColumnName = c.ColumnAlias; return true; } return false; } public bool IsCompatible(DbExpressionKind expressionKind) { switch (expressionKind) { case DbExpressionKind.Filter: return Where == null && Columns.Count == 0; case DbExpressionKind.Project: return Columns.Count == 0; case DbExpressionKind.Limit: return Limit == null; case DbExpressionKind.Skip: return Skip == null; case DbExpressionKind.Sort: return Columns.Count == 0 && GroupBy == null && OrderBy == null; case DbExpressionKind.GroupBy: return Columns.Count == 0 && GroupBy == null && OrderBy == null && Limit == null; } throw new InvalidOperationException(); } internal override void Accept(SqlFragmentVisitor visitor) { if (From != null) From.Accept(visitor); if (Columns != null) { foreach (ColumnFragment cf in Columns) { cf.Accept(visitor); } } if (Where != null) Where.Accept(visitor); if (Limit != null) Limit.Accept(visitor); if (Skip != null) Skip.Accept(visitor); if (GroupBy != null) { foreach (SqlFragment grp in GroupBy) { grp.Accept(visitor); } } if (OrderBy != null) { foreach (SortFragment sort in OrderBy) { sort.Accept(visitor); } } visitor.Visit(this); } } }