You've already forked linux-packaging-mono
Imported Upstream version 5.2.0.179
Former-commit-id: a536d4f20e27294d8bbc2184d75f3a22364f7ba1
This commit is contained in:
parent
966bba02bb
commit
fad71374d0
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the sql fragment for any node in the query tree.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The nodes in a query tree produce various kinds of sql
|
||||
/// <list type="bullet">
|
||||
/// <item>A select statement.</item>
|
||||
/// <item>A reference to an extent. (symbol)</item>
|
||||
/// <item>A raw string.</item>
|
||||
/// </list>
|
||||
/// We have this interface to allow for a common return type for the methods
|
||||
/// in the expression visitor <see cref="DbExpressionVisitor{T}"/>
|
||||
///
|
||||
/// At the end of translation, the sql fragments are converted into real strings.
|
||||
/// </remarks>
|
||||
internal interface ISqlFragment
|
||||
{
|
||||
/// <summary>
|
||||
/// Write the string represented by this fragment into the stream.
|
||||
/// </summary>
|
||||
/// <param name="writer">The stream that collects the strings.</param>
|
||||
/// <param name="sqlGenerator">Context information used for renaming.
|
||||
/// The global lists are used to generated new names without collisions.</param>
|
||||
void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// A Join symbol is a special kind of Symbol.
|
||||
/// It has to carry additional information
|
||||
/// <list type="bullet">
|
||||
/// <item>ColumnList for the list of columns in the select clause if this
|
||||
/// symbol represents a sql select statement. This is set by <see cref="SqlGenerator.AddDefaultColumns"/>. </item>
|
||||
/// <item>ExtentList is the list of extents in the select clause.</item>
|
||||
/// <item>FlattenedExtentList - if the Join has multiple extents flattened at the
|
||||
/// top level, we need this information to ensure that extent aliases are renamed
|
||||
/// correctly in <see cref="SqlSelectStatement.WriteSql"/></item>
|
||||
/// <item>NameToExtent has all the extents in ExtentList as a dictionary.
|
||||
/// This is used by <see cref="SqlGenerator.Visit(DbPropertyExpression)"/> to flatten
|
||||
/// record accesses.</item>
|
||||
/// <item>IsNestedJoin - is used to determine whether a JoinSymbol is an
|
||||
/// ordinary join symbol, or one that has a corresponding SqlSelectStatement.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// All the lists are set exactly once, and then used for lookups/enumerated.
|
||||
/// </summary>
|
||||
internal sealed class JoinSymbol : Symbol
|
||||
{
|
||||
private List<Symbol> columnList;
|
||||
internal List<Symbol> ColumnList
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == columnList)
|
||||
{
|
||||
columnList = new List<Symbol>();
|
||||
}
|
||||
return columnList;
|
||||
}
|
||||
set { columnList = value; }
|
||||
}
|
||||
|
||||
private List<Symbol> extentList;
|
||||
internal List<Symbol> ExtentList
|
||||
{
|
||||
get { return extentList; }
|
||||
}
|
||||
|
||||
private List<Symbol> flattenedExtentList;
|
||||
internal List<Symbol> FlattenedExtentList
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == flattenedExtentList)
|
||||
{
|
||||
flattenedExtentList = new List<Symbol>();
|
||||
}
|
||||
return flattenedExtentList;
|
||||
}
|
||||
set { flattenedExtentList = value; }
|
||||
}
|
||||
|
||||
private Dictionary<string, Symbol> nameToExtent;
|
||||
internal Dictionary<string, Symbol> NameToExtent
|
||||
{
|
||||
get { return nameToExtent; }
|
||||
}
|
||||
|
||||
private bool isNestedJoin;
|
||||
internal bool IsNestedJoin
|
||||
{
|
||||
get { return isNestedJoin; }
|
||||
set { isNestedJoin = value; }
|
||||
}
|
||||
|
||||
public JoinSymbol(string name, TypeUsage type, List<Symbol> extents)
|
||||
: base(name, type)
|
||||
{
|
||||
extentList = new List<Symbol>(extents.Count);
|
||||
nameToExtent = new Dictionary<string, Symbol>(extents.Count, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (Symbol symbol in extents)
|
||||
{
|
||||
this.nameToExtent[symbol.Name] = symbol;
|
||||
this.ExtentList.Add(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
101
external/entityframework/samples/Provider/SampleEntityFrameworkProvider/SqlGeneration/SqlBuilder.cs
vendored
Normal file
101
external/entityframework/samples/Provider/SampleEntityFrameworkProvider/SqlGeneration/SqlBuilder.cs
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is like StringBuilder. While traversing the tree for the first time,
|
||||
/// we do not know all the strings that need to be appended e.g. things that need to be
|
||||
/// renamed, nested select statements etc. So, we use a builder that can collect
|
||||
/// all kinds of sql fragments.
|
||||
/// </summary>
|
||||
internal sealed class SqlBuilder : ISqlFragment
|
||||
{
|
||||
private List<object> _sqlFragments;
|
||||
private List<object> sqlFragments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _sqlFragments)
|
||||
{
|
||||
_sqlFragments = new List<object>();
|
||||
}
|
||||
return _sqlFragments;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add an object to the list - we do not verify that it is a proper sql fragment
|
||||
/// since this is an internal method.
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
public void Append(object s)
|
||||
{
|
||||
Debug.Assert(s != null);
|
||||
sqlFragments.Add(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is to pretty print the SQL. The writer <see cref="SqlWriter.Write"/>
|
||||
/// needs to know about new lines so that it can add the right amount of
|
||||
/// indentation at the beginning of lines.
|
||||
/// </summary>
|
||||
public void AppendLine()
|
||||
{
|
||||
sqlFragments.Add("\r\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the builder is empty. This is used by the <see cref="SqlGenerator.Visit(DbProjectExpression)"/>
|
||||
/// to determine whether a sql statement can be reused.
|
||||
/// </summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get { return ((null == _sqlFragments) || (0 == _sqlFragments.Count)); }
|
||||
}
|
||||
|
||||
#region ISqlFragment Members
|
||||
|
||||
/// <summary>
|
||||
/// We delegate the writing of the fragment to the appropriate type.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="sqlGenerator"></param>
|
||||
public void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator)
|
||||
{
|
||||
if (null != _sqlFragments)
|
||||
{
|
||||
foreach (object o in _sqlFragments)
|
||||
{
|
||||
string str = (o as String);
|
||||
if (null != str)
|
||||
{
|
||||
writer.Write(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
ISqlFragment sqlFragment = (o as ISqlFragment);
|
||||
if (null != sqlFragment)
|
||||
{
|
||||
sqlFragment.WriteSql(writer, sqlGenerator);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
004b900742bebae9f8e3fdc3c947da685939f397
|
||||
@@ -0,0 +1,308 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <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 it does not correspond to anything in the DbCommandTree.
|
||||
/// Each of the fields is a SqlBuilder, so we can keep appending SQL strings
|
||||
/// or other fragments to build up the clause.
|
||||
///
|
||||
/// We have a IsDistinct property to indicate that we want distict 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.
|
||||
///
|
||||
/// 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
|
||||
{
|
||||
private bool isDistinct;
|
||||
|
||||
/// <summary>
|
||||
/// Do we need to add a DISTINCT at the beginning of the SELECT
|
||||
/// </summary>
|
||||
internal bool IsDistinct
|
||||
{
|
||||
get { return isDistinct; }
|
||||
set { isDistinct = value; }
|
||||
}
|
||||
|
||||
private List<Symbol> allJoinExtents;
|
||||
internal List<Symbol> AllJoinExtents
|
||||
{
|
||||
get { return allJoinExtents; }
|
||||
// We have a setter as well, even though this is a list,
|
||||
// since we use this field only in special cases.
|
||||
set { allJoinExtents = value; }
|
||||
}
|
||||
|
||||
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 TopClause top;
|
||||
internal TopClause Top
|
||||
{
|
||||
get { return top; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(top == null, "SqlSelectStatement.Top has already been set");
|
||||
top = value;
|
||||
}
|
||||
}
|
||||
|
||||
private SqlBuilder select = new SqlBuilder();
|
||||
internal SqlBuilder Select
|
||||
{
|
||||
get { return select; }
|
||||
}
|
||||
|
||||
private 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
|
||||
private bool isTopMost;
|
||||
internal bool IsTopMost
|
||||
{
|
||||
get { return this.isTopMost; }
|
||||
set { this.isTopMost = value; }
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
writer.Write("SELECT ");
|
||||
if (IsDistinct)
|
||||
{
|
||||
writer.Write("DISTINCT ");
|
||||
}
|
||||
|
||||
if (this.Top != null)
|
||||
{
|
||||
this.Top.WriteSql(writer, sqlGenerator);
|
||||
}
|
||||
|
||||
if ((null == this.select) || this.Select.IsEmpty)
|
||||
{
|
||||
Debug.Assert(false); // we have removed all possibilities of SELECT *.
|
||||
writer.Write("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
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.Top != null))
|
||||
{
|
||||
writer.WriteLine();
|
||||
writer.Write("ORDER BY ");
|
||||
this.OrderBy.WriteSql(writer, sqlGenerator);
|
||||
}
|
||||
|
||||
--writer.Indent;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// This extends StringWriter primarily to add the ability to add an indent
|
||||
/// to each line that is written out.
|
||||
/// </summary>
|
||||
class SqlWriter : StringWriter
|
||||
{
|
||||
// We start at -1, since the first select statement will increment it to 0.
|
||||
int indent = -1;
|
||||
/// <summary>
|
||||
/// The number of tabs to be added at the beginning of each new line.
|
||||
/// </summary>
|
||||
internal int Indent
|
||||
{
|
||||
get { return indent; }
|
||||
set { indent = value; }
|
||||
}
|
||||
|
||||
bool atBeginningOfLine = true;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
public SqlWriter(StringBuilder b)
|
||||
: base(b, System.Globalization.CultureInfo.InvariantCulture)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset atBeginningofLine if we detect the newline string.
|
||||
/// <see cref="SqlBuilder.AppendLine"/>
|
||||
/// Add as many tabs as the value of indent if we are at the
|
||||
/// beginning of a line.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public override void Write(string value)
|
||||
{
|
||||
if (value == "\r\n")
|
||||
{
|
||||
base.WriteLine();
|
||||
atBeginningOfLine = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (atBeginningOfLine)
|
||||
{
|
||||
if (indent > 0)
|
||||
{
|
||||
base.Write(new string('\t', indent));
|
||||
}
|
||||
atBeginningOfLine = false;
|
||||
}
|
||||
base.Write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override void WriteLine()
|
||||
{
|
||||
base.WriteLine();
|
||||
atBeginningOfLine = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
116
external/entityframework/samples/Provider/SampleEntityFrameworkProvider/SqlGeneration/Symbol.cs
vendored
Normal file
116
external/entityframework/samples/Provider/SampleEntityFrameworkProvider/SqlGeneration/Symbol.cs
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="SymbolTable"/>
|
||||
/// This class represents an extent/nested select statement,
|
||||
/// or a column.
|
||||
///
|
||||
/// The important fields are Name, Type and NewName.
|
||||
/// NewName starts off the same as Name, and is then modified as necessary.
|
||||
///
|
||||
///
|
||||
/// The rest are used by special symbols.
|
||||
/// e.g. NeedsRenaming is used by columns to indicate that a new name must
|
||||
/// be picked for the column in the second phase of translation.
|
||||
///
|
||||
/// IsUnnest is used by symbols for a collection expression used as a from clause.
|
||||
/// This allows <see cref="SqlGenerator.AddFromSymbol(SqlSelectStatement, string, Symbol, bool)"/> to add the column list
|
||||
/// after the alias.
|
||||
///
|
||||
/// </summary>
|
||||
class Symbol : ISqlFragment
|
||||
{
|
||||
private Dictionary<string, Symbol> columns = new Dictionary<string, Symbol>(StringComparer.CurrentCultureIgnoreCase);
|
||||
internal Dictionary<string, Symbol> Columns
|
||||
{
|
||||
get { return columns; }
|
||||
}
|
||||
|
||||
private bool needsRenaming = false;
|
||||
internal bool NeedsRenaming
|
||||
{
|
||||
get { return needsRenaming; }
|
||||
set { needsRenaming = value; }
|
||||
}
|
||||
|
||||
bool isUnnest = false;
|
||||
internal bool IsUnnest
|
||||
{
|
||||
get { return isUnnest; }
|
||||
set { isUnnest = value; }
|
||||
}
|
||||
|
||||
string name;
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
string newName;
|
||||
public string NewName
|
||||
{
|
||||
get { return newName; }
|
||||
set { newName = value; }
|
||||
}
|
||||
|
||||
private TypeUsage type;
|
||||
internal TypeUsage Type
|
||||
{
|
||||
get { return type; }
|
||||
set { type = value; }
|
||||
}
|
||||
|
||||
public Symbol(string name, TypeUsage type)
|
||||
{
|
||||
this.name = name;
|
||||
this.newName = name;
|
||||
this.Type = type;
|
||||
}
|
||||
|
||||
#region ISqlFragment Members
|
||||
|
||||
/// <summary>
|
||||
/// Write this symbol out as a string for sql. This is just
|
||||
/// the new name of the symbol (which could be the same as the old name).
|
||||
///
|
||||
/// We rename columns here if necessary.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="sqlGenerator"></param>
|
||||
public void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator)
|
||||
{
|
||||
if (this.NeedsRenaming)
|
||||
{
|
||||
string newName;
|
||||
int i = sqlGenerator.AllColumnNames[this.NewName];
|
||||
do
|
||||
{
|
||||
++i;
|
||||
newName = this.Name + i.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
} while (sqlGenerator.AllColumnNames.ContainsKey(newName));
|
||||
sqlGenerator.AllColumnNames[this.NewName] = i;
|
||||
|
||||
// Prevent it from being renamed repeatedly.
|
||||
this.NeedsRenaming = false;
|
||||
this.NewName = newName;
|
||||
|
||||
// Add this column name to list of known names so that there are no subsequent
|
||||
// collisions
|
||||
sqlGenerator.AllColumnNames[newName] = 0;
|
||||
}
|
||||
writer.Write(SqlGenerator.QuoteIdentifier(this.NewName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The SymbolPair exists to solve the record flattening problem.
|
||||
/// <see cref="SqlGenerator.Visit(DbPropertyExpression)"/>
|
||||
/// Consider a property expression D(v, "j3.j2.j1.a.x")
|
||||
/// where v is a VarRef, j1, j2, j3 are joins, a is an extent and x is a columns.
|
||||
/// This has to be translated eventually into {j'}.{x'}
|
||||
///
|
||||
/// The source field represents the outermost SqlStatement representing a join
|
||||
/// expression (say j2) - this is always a Join symbol.
|
||||
///
|
||||
/// The column field keeps moving from one join symbol to the next, until it
|
||||
/// stops at a non-join symbol.
|
||||
///
|
||||
/// This is returned by <see cref="SqlGenerator.Visit(DbPropertyExpression)"/>,
|
||||
/// but never makes it into a SqlBuilder.
|
||||
/// </summary>
|
||||
class SymbolPair : ISqlFragment
|
||||
{
|
||||
public Symbol Source;
|
||||
public Symbol Column;
|
||||
|
||||
public SymbolPair(Symbol source, Symbol column)
|
||||
{
|
||||
this.Source = source;
|
||||
this.Column = column;
|
||||
}
|
||||
|
||||
#region ISqlFragment Members
|
||||
|
||||
public void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator)
|
||||
{
|
||||
// Symbol pair should never be part of a SqlBuilder.
|
||||
Debug.Assert(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The symbol table is quite primitive - it is a stack with a new entry for
|
||||
/// each scope. Lookups search from the top of the stack to the bottom, until
|
||||
/// an entry is found.
|
||||
///
|
||||
/// The symbols are of the following kinds
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="Symbol"/> represents tables (extents/nested selects/unnests)</item>
|
||||
/// <item><see cref="JoinSymbol"/> represents Join nodes</item>
|
||||
/// <item><see cref="Symbol"/> columns.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// Symbols represent names <see cref="SqlGenerator.Visit(DbVariableReferenceExpression)"/> to be resolved,
|
||||
/// or things to be renamed.
|
||||
/// </summary>
|
||||
internal sealed class SymbolTable
|
||||
{
|
||||
private List<Dictionary<string, Symbol>> symbols = new List<Dictionary<string, Symbol>>();
|
||||
|
||||
internal void EnterScope()
|
||||
{
|
||||
symbols.Add(new Dictionary<string, Symbol>(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
internal void ExitScope()
|
||||
{
|
||||
symbols.RemoveAt(symbols.Count - 1);
|
||||
}
|
||||
|
||||
internal void Add(string name, Symbol value)
|
||||
{
|
||||
symbols[symbols.Count - 1][name] = value;
|
||||
}
|
||||
|
||||
internal Symbol Lookup(string name)
|
||||
{
|
||||
for (int i = symbols.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (symbols[i].ContainsKey(name))
|
||||
{
|
||||
return symbols[i][name];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.CommandTrees;
|
||||
|
||||
namespace SampleEntityFrameworkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// TopClause represents the a TOP expression in a SqlSelectStatement.
|
||||
/// It has a count property, which indicates how many TOP rows should be selected and a
|
||||
/// boolen WithTies property.
|
||||
/// </summary>
|
||||
class TopClause : ISqlFragment
|
||||
{
|
||||
ISqlFragment topCount;
|
||||
bool withTies;
|
||||
|
||||
/// <summary>
|
||||
/// Do we need to add a WITH_TIES to the top statement
|
||||
/// </summary>
|
||||
internal bool WithTies
|
||||
{
|
||||
get { return withTies; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How many top rows should be selected.
|
||||
/// </summary>
|
||||
internal ISqlFragment TopCount
|
||||
{
|
||||
get { return topCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a TopClause with the given topCount and withTies.
|
||||
/// </summary>
|
||||
/// <param name="topCount"></param>
|
||||
/// <param name="withTies"></param>
|
||||
internal TopClause(ISqlFragment topCount, bool withTies)
|
||||
{
|
||||
this.topCount = topCount;
|
||||
this.withTies = withTies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a TopClause with the given topCount and withTies.
|
||||
/// </summary>
|
||||
/// <param name="topCount"></param>
|
||||
/// <param name="withTies"></param>
|
||||
internal TopClause(int topCount, bool withTies)
|
||||
{
|
||||
SqlBuilder sqlBuilder = new SqlBuilder();
|
||||
sqlBuilder.Append(topCount.ToString(CultureInfo.InvariantCulture));
|
||||
this.topCount = sqlBuilder;
|
||||
this.withTies = withTies;
|
||||
}
|
||||
|
||||
#region ISqlFragment Members
|
||||
|
||||
/// <summary>
|
||||
/// Write out the TOP part of sql select statement
|
||||
/// It basically writes TOP (X) [WITH TIES].
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
/// <param name="sqlGenerator"></param>
|
||||
public void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator)
|
||||
{
|
||||
writer.Write("TOP (");
|
||||
this.TopCount.WriteSql(writer, sqlGenerator);
|
||||
writer.Write(")");
|
||||
|
||||
writer.Write(" ");
|
||||
|
||||
if (this.WithTies)
|
||||
{
|
||||
writer.Write("WITH TIES ");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user