You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,108 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="AliasedSlot.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Linq;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates a slot in a particular cql block.
|
||||
/// </summary>
|
||||
internal sealed class QualifiedSlot : ProjectedSlot
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a qualified slot "block_alias.slot_alias"
|
||||
/// </summary>
|
||||
internal QualifiedSlot(CqlBlock block, ProjectedSlot slot)
|
||||
{
|
||||
Debug.Assert(block != null && slot != null, "Null input to QualifiedSlot constructor");
|
||||
m_block = block;
|
||||
m_slot = slot; // Note: slot can be another qualified slot.
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly CqlBlock m_block;
|
||||
private readonly ProjectedSlot m_slot;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Creates new <see cref="ProjectedSlot"/> that is qualified with <paramref name="block"/>.CqlAlias.
|
||||
/// If current slot is composite (such as <see cref="CaseStatementProjectedSlot"/>, then this method recursively qualifies all parts
|
||||
/// and returns a new deeply qualified slot (as opposed to <see cref="CqlBlock.QualifySlotWithBlockAlias"/>).
|
||||
/// </summary>
|
||||
internal override ProjectedSlot DeepQualify(CqlBlock block)
|
||||
{
|
||||
// We take the slot inside this and change the block
|
||||
QualifiedSlot result = new QualifiedSlot(block, m_slot);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegates alias generation to the leaf slot in the qualified chain.
|
||||
/// </summary>
|
||||
internal override string GetCqlFieldAlias(MemberPath outputMember)
|
||||
{
|
||||
// Keep looking inside the chain of qualified slots till we find a non-qualified slot and then get the alias name for it.
|
||||
string result = GetOriginalSlot().GetCqlFieldAlias(outputMember);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walks the chain of <see cref="QualifiedSlot"/>s starting from the current one and returns the original slot.
|
||||
/// </summary>
|
||||
internal ProjectedSlot GetOriginalSlot()
|
||||
{
|
||||
ProjectedSlot slot = m_slot;
|
||||
while (true)
|
||||
{
|
||||
QualifiedSlot qualifiedSlot = slot as QualifiedSlot;
|
||||
if (qualifiedSlot == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
slot = qualifiedSlot.m_slot;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
internal string GetQualifiedCqlName(MemberPath outputMember)
|
||||
{
|
||||
return CqlWriter.GetQualifiedName(m_block.CqlAlias, GetCqlFieldAlias(outputMember));
|
||||
}
|
||||
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel)
|
||||
{
|
||||
Debug.Assert(blockAlias == null || m_block.CqlAlias == blockAlias, "QualifiedSlot: blockAlias mismatch");
|
||||
builder.Append(GetQualifiedCqlName(outputMember));
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
return m_block.GetInput(row).Property(GetCqlFieldAlias(outputMember));
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
StringUtil.FormatStringBuilder(builder, "{0} ", m_block.CqlAlias);
|
||||
m_slot.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="BooleanProjectedSlot.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents slots for expressions over boolean variables, e.g., _from0, _from1, etc
|
||||
/// </summary>
|
||||
internal sealed class BooleanProjectedSlot : ProjectedSlot
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a boolean slot for expression that comes from originalCellNum, i.e.,
|
||||
/// the value of the slot is <paramref name="expr"/> and the name is "_from{<paramref name="originalCellNum"/>}", e.g., _from2
|
||||
/// </summary>
|
||||
internal BooleanProjectedSlot(BoolExpression expr, CqlIdentifiers identifiers, int originalCellNum)
|
||||
{
|
||||
m_expr = expr;
|
||||
m_originalCell = new CellIdBoolean(identifiers, originalCellNum);
|
||||
|
||||
Debug.Assert(!(expr.AsLiteral is CellIdBoolean) ||
|
||||
BoolLiteral.EqualityComparer.Equals((CellIdBoolean)expr.AsLiteral, m_originalCell), "Cellid boolean for the slot and cell number disagree");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The actual value of the slot - could be <see cref="CellIdBoolean"/>!
|
||||
/// </summary>
|
||||
private readonly BoolExpression m_expr;
|
||||
/// <summary>
|
||||
/// A boolean corresponding to the original cell number (_from0)
|
||||
/// </summary>
|
||||
private readonly CellIdBoolean m_originalCell;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Returns "_from0", "_from1" etc. <paramref name="outputMember"/> is ignored.
|
||||
/// </summary>
|
||||
internal override string GetCqlFieldAlias(MemberPath outputMember)
|
||||
{
|
||||
return m_originalCell.SlotName;
|
||||
}
|
||||
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel)
|
||||
{
|
||||
if (m_expr.IsTrue || m_expr.IsFalse)
|
||||
{
|
||||
// No Case statement for TRUE and FALSE
|
||||
m_expr.AsEsql(builder, blockAlias);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Produce "CASE WHEN boolExpr THEN True ELSE False END" in order to enforce the two-state boolean logic:
|
||||
// if boolExpr returns the boolean Unknown, it gets converted to boolean False.
|
||||
builder.Append("CASE WHEN ");
|
||||
m_expr.AsEsql(builder, blockAlias);
|
||||
builder.Append(" THEN True ELSE False END");
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
if (m_expr.IsTrue || m_expr.IsFalse)
|
||||
{
|
||||
return m_expr.AsCqt(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Produce "CASE WHEN boolExpr THEN True ELSE False END" in order to enforce the two-state boolean logic:
|
||||
// if boolExpr returns the boolean Unknown, it gets converted to boolean False.
|
||||
return DbExpressionBuilder.Case(new DbExpression[] { m_expr.AsCqt(row) }, new DbExpression[] { DbExpressionBuilder.True }, DbExpressionBuilder.False);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
StringUtil.FormatStringBuilder(builder, "<{0}, ", m_originalCell.SlotName);
|
||||
m_expr.ToCompactString(builder);
|
||||
builder.Append('>');
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CaseCqlBlock.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to capture cql blocks responsible for case statements generating multiconstants, i.e., complex types, entities, discriminators, etc.
|
||||
/// </summary>
|
||||
internal sealed class CaseCqlBlock : CqlBlock
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Creates a <see cref="CqlBlock"/> containing the case statememt for the <paramref name="caseSlot"/> and projecting other slots as is from its child (input). CqlBlock with SELECT (slots),
|
||||
/// </summary>
|
||||
/// <param name="caseSlot">indicates which slot in <paramref name="slots"/> corresponds to the case statement being generated by this block</param>
|
||||
internal CaseCqlBlock(SlotInfo[] slots, int caseSlot, CqlBlock child, BoolExpression whereClause, CqlIdentifiers identifiers, int blockAliasNum)
|
||||
: base(slots, new List<CqlBlock>(new CqlBlock[] { child }), whereClause, identifiers, blockAliasNum)
|
||||
{
|
||||
m_caseSlotInfo = slots[caseSlot];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly SlotInfo m_caseSlotInfo;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, bool isTopLevel, int indentLevel)
|
||||
{
|
||||
// The SELECT part
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
builder.Append("SELECT ");
|
||||
if (isTopLevel)
|
||||
{
|
||||
builder.Append("VALUE ");
|
||||
}
|
||||
Debug.Assert(m_caseSlotInfo.OutputMember != null, "We only construct member slots, not boolean slots.");
|
||||
builder.Append("-- Constructing ").Append(m_caseSlotInfo.OutputMember.LeafName);
|
||||
|
||||
Debug.Assert(Children.Count == 1, "CaseCqlBlock can have exactly one child.");
|
||||
CqlBlock childBlock = Children[0];
|
||||
|
||||
base.GenerateProjectionEsql(builder, childBlock.CqlAlias, true, indentLevel, isTopLevel);
|
||||
|
||||
// The FROM part: FROM (ChildView) AS AliasName
|
||||
builder.Append("FROM (");
|
||||
childBlock.AsEsql(builder, false, indentLevel + 1);
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
builder.Append(") AS ").Append(childBlock.CqlAlias);
|
||||
|
||||
// Get the WHERE part only when the expression is not simply TRUE.
|
||||
if (false == BoolExpression.EqualityComparer.Equals(this.WhereClause, BoolExpression.True))
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
builder.Append("WHERE ");
|
||||
this.WhereClause.AsEsql(builder, childBlock.CqlAlias);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(bool isTopLevel)
|
||||
{
|
||||
Debug.Assert(m_caseSlotInfo.OutputMember != null, "We only construct real slots not boolean slots");
|
||||
|
||||
// The FROM part: FROM (childBlock)
|
||||
Debug.Assert(Children.Count == 1, "CaseCqlBlock can have exactly one child.");
|
||||
CqlBlock childBlock = this.Children[0];
|
||||
DbExpression cqt = childBlock.AsCqt(false);
|
||||
|
||||
// Get the WHERE part only when the expression is not simply TRUE.
|
||||
if (!BoolExpression.EqualityComparer.Equals(this.WhereClause, BoolExpression.True))
|
||||
{
|
||||
cqt = cqt.Where(row => this.WhereClause.AsCqt(row));
|
||||
}
|
||||
|
||||
// The SELECT part.
|
||||
return cqt.Select(row => GenerateProjectionCqt(row, isTopLevel));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CqlBlock.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Linq;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that holds an expression of the form "(SELECT .. FROM .. WHERE) AS alias".
|
||||
/// Essentially, it allows generating Cql query in a localized manner, i.e., all global decisions about nulls, constants,
|
||||
/// case statements, etc have already been made.
|
||||
/// </summary>
|
||||
internal abstract class CqlBlock : InternalBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="CqlBlock"/> with the SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>),
|
||||
/// WHERE (<paramref name="whereClause"/>), AS (<paramref name="blockAliasNum"/>).
|
||||
/// </summary>
|
||||
protected CqlBlock(SlotInfo[] slotInfos, List<CqlBlock> children, BoolExpression whereClause, CqlIdentifiers identifiers, int blockAliasNum)
|
||||
{
|
||||
m_slots = new ReadOnlyCollection<SlotInfo>(slotInfos);
|
||||
m_children = new ReadOnlyCollection<CqlBlock>(children);
|
||||
m_whereClause = whereClause;
|
||||
m_blockAlias = identifiers.GetBlockAlias(blockAliasNum);
|
||||
}
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// Essentially, SELECT. May be replaced with another collection after block construction.
|
||||
/// </summary>
|
||||
private ReadOnlyCollection<SlotInfo> m_slots;
|
||||
/// <summary>
|
||||
/// FROM inputs.
|
||||
/// </summary>
|
||||
private readonly ReadOnlyCollection<CqlBlock> m_children;
|
||||
/// <summary>
|
||||
/// WHERER.
|
||||
/// </summary>
|
||||
private readonly BoolExpression m_whereClause;
|
||||
/// <summary>
|
||||
/// Alias of the whole block for cql generation.
|
||||
/// </summary>
|
||||
private readonly string m_blockAlias;
|
||||
/// <summary>
|
||||
/// See <see cref="JoinTreeContext"/> for more info.
|
||||
/// </summary>
|
||||
private JoinTreeContext m_joinTreeContext;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns all the slots for this block (SELECT).
|
||||
/// </summary>
|
||||
internal ReadOnlyCollection<SlotInfo> Slots
|
||||
{
|
||||
get { return m_slots; }
|
||||
set { m_slots = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the child (input) blocks of this block (FROM).
|
||||
/// </summary>
|
||||
protected ReadOnlyCollection<CqlBlock> Children
|
||||
{
|
||||
get { return m_children; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the where clause of this block (WHERE).
|
||||
/// </summary>
|
||||
protected BoolExpression WhereClause
|
||||
{
|
||||
get { return m_whereClause; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an alias for this block that can be used for "AS".
|
||||
/// </summary>
|
||||
internal string CqlAlias
|
||||
{
|
||||
get { return m_blockAlias; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Abstract Methods
|
||||
/// <summary>
|
||||
/// Returns a string corresponding to the eSQL representation of this block (and its children below).
|
||||
/// </summary>
|
||||
internal abstract StringBuilder AsEsql(StringBuilder builder, bool isTopLevel, int indentLevel);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string corresponding to the CQT representation of this block (and its children below).
|
||||
/// </summary>
|
||||
internal abstract DbExpression AsCqt(bool isTopLevel);
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// For the given <paramref name="slotNum"/> creates a <see cref="QualifiedSlot"/> qualified with <see cref="CqlAlias"/> of the current block:
|
||||
/// "<see cref="CqlAlias"/>.slot_alias"
|
||||
/// </summary>
|
||||
internal QualifiedSlot QualifySlotWithBlockAlias(int slotNum)
|
||||
{
|
||||
Debug.Assert(this.IsProjected(slotNum), StringUtil.FormatInvariant("Slot {0} that is to be qualified with the block alias is not projected in this block", slotNum));
|
||||
var slotInfo = m_slots[slotNum];
|
||||
return new QualifiedSlot(this, slotInfo.SlotValue);
|
||||
}
|
||||
|
||||
internal ProjectedSlot SlotValue(int slotNum)
|
||||
{
|
||||
Debug.Assert(slotNum < m_slots.Count, "Slotnum too high");
|
||||
return m_slots[slotNum].SlotValue;
|
||||
}
|
||||
|
||||
internal MemberPath MemberPath(int slotNum)
|
||||
{
|
||||
Debug.Assert(slotNum < m_slots.Count, "Slotnum too high");
|
||||
return m_slots[slotNum].OutputMember;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true iff <paramref name="slotNum"/> is being projected by this block.
|
||||
/// </summary>
|
||||
internal bool IsProjected(int slotNum)
|
||||
{
|
||||
Debug.Assert(slotNum < m_slots.Count, "Slotnum too high");
|
||||
return m_slots[slotNum].IsProjected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates "A, B, C, ..." for all the slots in the block.
|
||||
/// </summary>
|
||||
protected void GenerateProjectionEsql(StringBuilder builder, string blockAlias, bool addNewLineAfterEachSlot, int indentLevel, bool isTopLevel)
|
||||
{
|
||||
bool isFirst = true;
|
||||
foreach (SlotInfo slotInfo in Slots)
|
||||
{
|
||||
if (false == slotInfo.IsRequiredByParent)
|
||||
{
|
||||
// Ignore slots that are not needed
|
||||
continue;
|
||||
}
|
||||
if (isFirst == false)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
if (addNewLineAfterEachSlot)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 1);
|
||||
}
|
||||
|
||||
slotInfo.AsEsql(builder, blockAlias, indentLevel);
|
||||
|
||||
// Print the field alias for complex expressions that don't produce default alias.
|
||||
// Don't print alias for qualified fields as they reproduce their alias.
|
||||
// Don't print alias if it's a top level query using SELECT VALUE.
|
||||
if (!isTopLevel && (!(slotInfo.SlotValue is QualifiedSlot) || slotInfo.IsEnforcedNotNull))
|
||||
{
|
||||
builder.Append(" AS ")
|
||||
.Append(slotInfo.CqlFieldAlias);
|
||||
}
|
||||
isFirst = false;
|
||||
}
|
||||
if (addNewLineAfterEachSlot)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates "NewRow(A, B, C, ...)" for all the slots in the block.
|
||||
/// If <paramref name="isTopLevel"/>=true then generates "A" for the only slot that is marked as <see cref="SlotInfo.IsRequiredByParent"/>.
|
||||
/// </summary>
|
||||
protected DbExpression GenerateProjectionCqt(DbExpression row, bool isTopLevel)
|
||||
{
|
||||
if (isTopLevel)
|
||||
{
|
||||
Debug.Assert(this.Slots.Where(slot => slot.IsRequiredByParent).Count() == 1, "Top level projection must project only one slot.");
|
||||
return this.Slots.Where(slot => slot.IsRequiredByParent).Single().AsCqt(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DbExpressionBuilder.NewRow(
|
||||
this.Slots.Where(slot => slot.IsRequiredByParent).Select(slot => new KeyValuePair<string, DbExpression>(slot.CqlFieldAlias, slot.AsCqt(row))));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes context positioning in the join tree that owns the <see cref="CqlBlock"/>.
|
||||
/// For more info see <see cref="JoinTreeContext"/>.
|
||||
/// </summary>
|
||||
internal void SetJoinTreeContext(IList<string> parentQualifiers, string leafQualifier)
|
||||
{
|
||||
Debug.Assert(m_joinTreeContext == null, "Join tree context is already set.");
|
||||
m_joinTreeContext = new JoinTreeContext(parentQualifiers, leafQualifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the input <paramref name="row"/> for the property that represents the current <see cref="CqlBlock"/>.
|
||||
/// In all cases except JOIN, the <paramref name="row"/> is returned as is.
|
||||
/// In case of JOIN, <paramref name="row"/>.JoinVarX.JoinVarY...blockVar is returned.
|
||||
/// See <see cref="SetJoinTreeContext"/> for more info.
|
||||
/// </summary>
|
||||
internal DbExpression GetInput(DbExpression row)
|
||||
{
|
||||
return m_joinTreeContext != null ? m_joinTreeContext.FindInput(row) : row;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
for (int i = 0; i < m_slots.Count; i++)
|
||||
{
|
||||
StringUtil.FormatStringBuilder(builder, "{0}: ", i);
|
||||
m_slots[i].ToCompactString(builder);
|
||||
builder.Append(' ');
|
||||
}
|
||||
m_whereClause.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region JoinTreeContext
|
||||
/// <summary>
|
||||
/// The class represents a position of a <see cref="CqlBlock"/> in a join tree.
|
||||
/// It is expected that the join tree is left-recursive (not balanced) and looks like this:
|
||||
///
|
||||
/// ___J___
|
||||
/// / \
|
||||
/// L3/ \R3
|
||||
/// / \
|
||||
/// __J__ \
|
||||
/// / \ \
|
||||
/// L2/ \R2 \
|
||||
/// / \ \
|
||||
/// _J_ \ \
|
||||
/// / \ \ \
|
||||
/// L1/ \R1 \ \
|
||||
/// / \ \ \
|
||||
/// CqlBlock1 CqlBlock2 CqlBlock3 CqlBlock4
|
||||
///
|
||||
/// Example of <see cref="JoinTreeContext"/>s for the <see cref="CqlBlock"/>s:
|
||||
/// block# m_parentQualifiers m_indexInParentQualifiers m_leafQualifier FindInput(row) = ...
|
||||
/// 1 (L2, L3) 0 L1 row.(L3.L2).L1
|
||||
/// 2 (L2, L3) 0 R1 row.(L3.L2).R1
|
||||
/// 3 (L2, L3) 1 R2 row.(L3).R2
|
||||
/// 4 (L2, L3) 2 R3 row.().R3
|
||||
///
|
||||
/// </summary>
|
||||
private sealed class JoinTreeContext
|
||||
{
|
||||
internal JoinTreeContext(IList<string> parentQualifiers, string leafQualifier)
|
||||
{
|
||||
Debug.Assert(parentQualifiers != null, "parentQualifiers != null");
|
||||
Debug.Assert(leafQualifier != null, "leafQualifier != null");
|
||||
|
||||
m_parentQualifiers = parentQualifiers;
|
||||
m_indexInParentQualifiers = parentQualifiers.Count;
|
||||
m_leafQualifier = leafQualifier;
|
||||
}
|
||||
|
||||
private readonly IList<string> m_parentQualifiers;
|
||||
private readonly int m_indexInParentQualifiers;
|
||||
private readonly string m_leafQualifier;
|
||||
|
||||
internal DbExpression FindInput(DbExpression row)
|
||||
{
|
||||
DbExpression cqt = row;
|
||||
for (int i = m_parentQualifiers.Count - 1; i >= m_indexInParentQualifiers; --i)
|
||||
{
|
||||
cqt = cqt.Property(m_parentQualifiers[i]);
|
||||
}
|
||||
return cqt.Property(m_leafQualifier);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CqlIdentifiers.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
using System.Data.Common.Utils;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
// This class is responsible for ensuring unique aliases for _from0, etc
|
||||
// and block aliases T, T0, T1, etc
|
||||
internal class CqlIdentifiers : InternalBase
|
||||
{
|
||||
|
||||
#region Constructor
|
||||
internal CqlIdentifiers()
|
||||
{
|
||||
m_identifiers = new Set<string>(StringComparer.Ordinal);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private Set<string> m_identifiers;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
// effects: Given a number, returns _from<num> if it does not clashes with
|
||||
// any identifier, else returns _from_<next>_<num> where <next> is the first number from 0
|
||||
// where there is no clash
|
||||
internal string GetFromVariable(int num)
|
||||
{
|
||||
return GetNonConflictingName("_from", num);
|
||||
}
|
||||
|
||||
// effects: Given a number, returns T<num> if it does not clashes with
|
||||
// any identifier, else returns T_<next>_<num> where <next> is the first number from 0
|
||||
// where there is no clash
|
||||
internal string GetBlockAlias(int num)
|
||||
{
|
||||
return GetNonConflictingName("T", num);
|
||||
}
|
||||
|
||||
// effects: Given a number, returns T if it does not clashes with
|
||||
// any identifier, else returns T_<next> where <next> is the first number from 0
|
||||
// where there is no clash
|
||||
internal string GetBlockAlias()
|
||||
{
|
||||
return GetNonConflictingName("T", -1);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
|
||||
internal void AddIdentifier(string identifier)
|
||||
{
|
||||
m_identifiers.Add(identifier.ToLower(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
|
||||
private string GetNonConflictingName(string prefix, int number)
|
||||
{
|
||||
// Do a case sensitive search but return the string that uses the
|
||||
// original prefix
|
||||
string result = number < 0 ? prefix : StringUtil.FormatInvariant("{0}{1}", prefix, number);
|
||||
// Check if the prefix exists or not
|
||||
if (m_identifiers.Contains(result.ToLower(CultureInfo.InvariantCulture)) == false)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Go through integers and find the first one that does not clash
|
||||
for (int count = 0; count < int.MaxValue; count++)
|
||||
{
|
||||
if (number < 0)
|
||||
{
|
||||
result = StringUtil.FormatInvariant("{0}_{1}", prefix, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = StringUtil.FormatInvariant("{0}_{1}_{2}", prefix, count, number);
|
||||
}
|
||||
if (m_identifiers.Contains(result.ToLower(CultureInfo.InvariantCulture)) == false)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Debug.Fail("Found no unique _from till MaxValue?");
|
||||
return null;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
m_identifiers.ToCompactString(builder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CqlWriter.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Mapping.ViewGeneration.Utils;
|
||||
using System.Data.Metadata.Edm;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
|
||||
// This class contains helper methods needed for generating Cql
|
||||
internal static class CqlWriter
|
||||
{
|
||||
|
||||
#region Fields
|
||||
private static readonly Regex s_wordIdentifierRegex = new Regex(@"^[_A-Za-z]\w*$", RegexOptions.ECMAScript | RegexOptions.Compiled);
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
// effects: Given a block name and a field in it -- returns a string
|
||||
// of form "blockName.field". Does not perform any escaping
|
||||
internal static string GetQualifiedName(string blockName, string field)
|
||||
{
|
||||
string result = StringUtil.FormatInvariant("{0}.{1}", blockName, field);
|
||||
return result;
|
||||
}
|
||||
|
||||
// effects: Modifies builder to contain an escaped version of type's name as "[namespace.typename]"
|
||||
internal static void AppendEscapedTypeName(StringBuilder builder, EdmType type)
|
||||
{
|
||||
AppendEscapedName(builder, GetQualifiedName(type.NamespaceName, type.Name));
|
||||
}
|
||||
|
||||
// effects: Modifies builder to contain an escaped version of "name1.name2" as "[name1].[name2]"
|
||||
internal static void AppendEscapedQualifiedName(StringBuilder builder, string name1, string name2)
|
||||
{
|
||||
AppendEscapedName(builder, name1);
|
||||
builder.Append('.');
|
||||
AppendEscapedName(builder, name2);
|
||||
}
|
||||
|
||||
// effects: Modifies builder to contain an escaped version of "name"
|
||||
internal static void AppendEscapedName(StringBuilder builder, string name)
|
||||
{
|
||||
if (s_wordIdentifierRegex.IsMatch(name) && false == ExternalCalls.IsReservedKeyword(name))
|
||||
{
|
||||
// We do not need to escape the name if it is a simple name and it is not a keyword
|
||||
builder.Append(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
string newName = name.Replace("]", "]]");
|
||||
builder.Append('[')
|
||||
.Append(newName)
|
||||
.Append(']');
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="ExtentCqlBlock.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Metadata.Edm;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that represents leaf <see cref="CqlBlock"/>s in the <see cref="CqlBlock"/> tree.
|
||||
/// </summary>
|
||||
internal sealed class ExtentCqlBlock : CqlBlock
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Creates an cql block representing the <paramref name="extent"/> (the FROM part).
|
||||
/// SELECT is given by <paramref name="slots"/>, WHERE by <paramref name="whereClause"/> and AS by <paramref name="blockAliasNum"/>.
|
||||
/// </summary>
|
||||
internal ExtentCqlBlock(EntitySetBase extent,
|
||||
CellQuery.SelectDistinct selectDistinct,
|
||||
SlotInfo[] slots,
|
||||
BoolExpression whereClause,
|
||||
CqlIdentifiers identifiers,
|
||||
int blockAliasNum)
|
||||
: base(slots, EmptyChildren, whereClause, identifiers, blockAliasNum)
|
||||
{
|
||||
m_extent = extent;
|
||||
m_nodeTableAlias = identifiers.GetBlockAlias();
|
||||
m_selectDistinct = selectDistinct;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly EntitySetBase m_extent;
|
||||
private readonly string m_nodeTableAlias;
|
||||
private readonly CellQuery.SelectDistinct m_selectDistinct;
|
||||
private static readonly List<CqlBlock> EmptyChildren = new List<CqlBlock>();
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, bool isTopLevel, int indentLevel)
|
||||
{
|
||||
// The SELECT/DISTINCT part.
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
builder.Append("SELECT ");
|
||||
if (m_selectDistinct == CellQuery.SelectDistinct.Yes)
|
||||
{
|
||||
builder.Append("DISTINCT ");
|
||||
}
|
||||
GenerateProjectionEsql(builder, m_nodeTableAlias, true, indentLevel, isTopLevel);
|
||||
|
||||
// Get the FROM part.
|
||||
builder.Append("FROM ");
|
||||
CqlWriter.AppendEscapedQualifiedName(builder, m_extent.EntityContainer.Name, m_extent.Name);
|
||||
builder.Append(" AS ").Append(m_nodeTableAlias);
|
||||
|
||||
// Get the WHERE part only when the expression is not simply TRUE.
|
||||
if (!BoolExpression.EqualityComparer.Equals(this.WhereClause, BoolExpression.True))
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
builder.Append("WHERE ");
|
||||
this.WhereClause.AsEsql(builder, m_nodeTableAlias);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(bool isTopLevel)
|
||||
{
|
||||
// Get the FROM part.
|
||||
DbExpression cqt = m_extent.Scan();
|
||||
|
||||
// Get the WHERE part only when the expression is not simply TRUE.
|
||||
if (!BoolExpression.EqualityComparer.Equals(this.WhereClause, BoolExpression.True))
|
||||
{
|
||||
cqt = cqt.Where(row => this.WhereClause.AsCqt(row));
|
||||
}
|
||||
|
||||
// The SELECT/DISTINCT part.
|
||||
cqt = cqt.Select(row => GenerateProjectionCqt(row, isTopLevel));
|
||||
if (m_selectDistinct == CellQuery.SelectDistinct.Yes)
|
||||
{
|
||||
cqt = cqt.Distinct();
|
||||
}
|
||||
|
||||
return cqt;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="JoinCqlBlock.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents to the various Join nodes in the view: IJ, LOJ, FOJ.
|
||||
/// </summary>
|
||||
internal sealed class JoinCqlBlock : CqlBlock
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a join block (type given by <paramref name="opType"/>) with SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>),
|
||||
/// ON (<paramref name="onClauses"/> - one for each child except 0th), WHERE (true), AS (<paramref name="blockAliasNum"/>).
|
||||
/// </summary>
|
||||
internal JoinCqlBlock(CellTreeOpType opType,
|
||||
SlotInfo[] slotInfos,
|
||||
List<CqlBlock> children,
|
||||
List<OnClause> onClauses,
|
||||
CqlIdentifiers identifiers,
|
||||
int blockAliasNum)
|
||||
: base(slotInfos, children, BoolExpression.True, identifiers, blockAliasNum)
|
||||
{
|
||||
m_opType = opType;
|
||||
m_onClauses = onClauses;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly CellTreeOpType m_opType;
|
||||
private readonly List<OnClause> m_onClauses;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, bool isTopLevel, int indentLevel)
|
||||
{
|
||||
// The SELECT part.
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
builder.Append("SELECT ");
|
||||
GenerateProjectionEsql(
|
||||
builder,
|
||||
null, /* There is no single input, so the blockAlias is null. ProjectedSlot objects will have to carry their own input block info:
|
||||
* see QualifiedSlot and QualifiedCellIdBoolean for more info. */
|
||||
false,
|
||||
indentLevel,
|
||||
isTopLevel);
|
||||
StringUtil.IndentNewLine(builder, indentLevel);
|
||||
|
||||
// The FROM part by joining all the children using ON Clauses.
|
||||
builder.Append("FROM ");
|
||||
int i = 0;
|
||||
foreach (CqlBlock child in Children)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 1);
|
||||
builder.Append(OpCellTreeNode.OpToEsql(m_opType));
|
||||
}
|
||||
builder.Append(" (");
|
||||
child.AsEsql(builder, false, indentLevel + 1);
|
||||
builder.Append(") AS ")
|
||||
.Append(child.CqlAlias);
|
||||
|
||||
// The ON part.
|
||||
if (i > 0)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 1);
|
||||
builder.Append("ON ");
|
||||
m_onClauses[i - 1].AsEsql(builder);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(bool isTopLevel)
|
||||
{
|
||||
// The FROM part:
|
||||
// - build a tree of binary joins out of the inputs (this.Children).
|
||||
// - update each child block with its relative position in the join tree,
|
||||
// so that QualifiedSlot and QualifiedCellIdBoolean objects could find their
|
||||
// designated block areas inside the cumulative join row passed into their AsCqt(row) method.
|
||||
CqlBlock leftmostBlock = this.Children[0];
|
||||
DbExpression left = leftmostBlock.AsCqt(false);
|
||||
List<string> joinTreeCtxParentQualifiers = new List<string>();
|
||||
for (int i = 1; i < this.Children.Count; ++i)
|
||||
{
|
||||
// Join the current left expression (a tree) to the current right block.
|
||||
CqlBlock rightBlock = this.Children[i];
|
||||
DbExpression right = rightBlock.AsCqt(false);
|
||||
Func<DbExpression, DbExpression, DbExpression> joinConditionFunc = m_onClauses[i - 1].AsCqt;
|
||||
DbJoinExpression join;
|
||||
switch (m_opType)
|
||||
{
|
||||
case CellTreeOpType.FOJ:
|
||||
join = left.FullOuterJoin(right, joinConditionFunc);
|
||||
break;
|
||||
case CellTreeOpType.IJ:
|
||||
join = left.InnerJoin(right, joinConditionFunc);
|
||||
break;
|
||||
case CellTreeOpType.LOJ:
|
||||
join = left.LeftOuterJoin(right, joinConditionFunc);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail("Unknown operator");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
// Assign the joinTreeContext to the leftmost block.
|
||||
leftmostBlock.SetJoinTreeContext(joinTreeCtxParentQualifiers, join.Left.VariableName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the joinTreeCtxParentQualifiers.
|
||||
// Note that all blocks that already participate in the left expression tree share the same copy of the joinTreeContext.
|
||||
joinTreeCtxParentQualifiers.Add(join.Left.VariableName);
|
||||
}
|
||||
|
||||
// Assign the joinTreeContext to the right block.
|
||||
rightBlock.SetJoinTreeContext(joinTreeCtxParentQualifiers, join.Right.VariableName);
|
||||
|
||||
left = join;
|
||||
}
|
||||
|
||||
// The SELECT part.
|
||||
return left.Select(row => GenerateProjectionCqt(row, false));
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Represents a complete ON clause "slot1 == slot2 AND "slot3 == slot4" ... for two <see cref="JoinCqlBlock"/>s.
|
||||
/// </summary>
|
||||
internal sealed class OnClause : InternalBase
|
||||
{
|
||||
#region Constructor
|
||||
internal OnClause()
|
||||
{
|
||||
m_singleClauses = new List<SingleClause>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly List<SingleClause> m_singleClauses;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Adds an <see cref="SingleClause"/> element for a join of the form <paramref name="leftSlot"/> = <paramref name="rightSlot"/>.
|
||||
/// </summary>
|
||||
internal void Add(QualifiedSlot leftSlot, MemberPath leftSlotOutputMember, QualifiedSlot rightSlot, MemberPath rightSlotOutputMember)
|
||||
{
|
||||
SingleClause singleClause = new SingleClause(leftSlot, leftSlotOutputMember, rightSlot, rightSlotOutputMember);
|
||||
m_singleClauses.Add(singleClause);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates eSQL string of the form "LeftSlot1 = RightSlot1 AND LeftSlot2 = RightSlot2 AND ...
|
||||
/// </summary>
|
||||
internal StringBuilder AsEsql(StringBuilder builder)
|
||||
{
|
||||
bool isFirst = true;
|
||||
foreach (SingleClause singleClause in m_singleClauses)
|
||||
{
|
||||
if (false == isFirst)
|
||||
{
|
||||
builder.Append(" AND ");
|
||||
}
|
||||
singleClause.AsEsql(builder);
|
||||
isFirst = false;
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates CQT of the form "LeftSlot1 = RightSlot1 AND LeftSlot2 = RightSlot2 AND ...
|
||||
/// </summary>
|
||||
internal DbExpression AsCqt(DbExpression leftRow, DbExpression rightRow)
|
||||
{
|
||||
DbExpression cqt = m_singleClauses[0].AsCqt(leftRow, rightRow);
|
||||
for (int i = 1; i < m_singleClauses.Count; ++i)
|
||||
{
|
||||
cqt = cqt.And(m_singleClauses[i].AsCqt(leftRow, rightRow));
|
||||
}
|
||||
return cqt;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append("ON ");
|
||||
StringUtil.ToSeparatedString(builder, m_singleClauses, " AND ");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SingleClause
|
||||
/// <summary>
|
||||
/// Represents an expression between slots of the form: LeftSlot = RightSlot
|
||||
/// </summary>
|
||||
private sealed class SingleClause : InternalBase
|
||||
{
|
||||
internal SingleClause(QualifiedSlot leftSlot, MemberPath leftSlotOutputMember, QualifiedSlot rightSlot, MemberPath rightSlotOutputMember)
|
||||
{
|
||||
m_leftSlot = leftSlot;
|
||||
m_leftSlotOutputMember = leftSlotOutputMember;
|
||||
m_rightSlot = rightSlot;
|
||||
m_rightSlotOutputMember = rightSlotOutputMember;
|
||||
}
|
||||
|
||||
#region Fields
|
||||
private readonly QualifiedSlot m_leftSlot;
|
||||
private readonly MemberPath m_leftSlotOutputMember;
|
||||
private readonly QualifiedSlot m_rightSlot;
|
||||
private readonly MemberPath m_rightSlotOutputMember;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Generates eSQL string of the form "leftSlot = rightSlot".
|
||||
/// </summary>
|
||||
internal StringBuilder AsEsql(StringBuilder builder)
|
||||
{
|
||||
builder.Append(m_leftSlot.GetQualifiedCqlName(m_leftSlotOutputMember))
|
||||
.Append(" = ")
|
||||
.Append(m_rightSlot.GetQualifiedCqlName(m_rightSlotOutputMember));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates CQT of the form "leftSlot = rightSlot".
|
||||
/// </summary>
|
||||
internal DbExpression AsCqt(DbExpression leftRow, DbExpression rightRow)
|
||||
{
|
||||
return m_leftSlot.AsCqt(leftRow, m_leftSlotOutputMember).Equal(m_rightSlot.AsCqt(rightRow, m_rightSlotOutputMember));
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
m_leftSlot.ToCompactString(builder);
|
||||
builder.Append(" = ");
|
||||
m_rightSlot.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="SlotInfo.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that keeps track of slot information in a <see cref="CqlBlock"/>.
|
||||
/// </summary>
|
||||
internal sealed class SlotInfo : InternalBase
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SlotInfo"/> for a <see cref="CqlBlock"/> X with information about whether this slot is needed by X's parent
|
||||
/// (<paramref name="isRequiredByParent"/>), whether X projects it (<paramref name="isProjected"/>) along with the slot value (<paramref name="slotValue"/>) and
|
||||
/// the output member path (<paramref name="outputMember"/> (for regular/non-boolean slots) for the slot.
|
||||
/// </summary>
|
||||
internal SlotInfo(bool isRequiredByParent, bool isProjected, ProjectedSlot slotValue, MemberPath outputMember)
|
||||
: this(isRequiredByParent, isProjected, slotValue, outputMember, false /* enforceNotNull */)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SlotInfo"/> for a <see cref="CqlBlock"/> X with information about whether this slot is needed by X's parent
|
||||
/// (<paramref name="isRequiredByParent"/>), whether X projects it (<paramref name="isProjected"/>) along with the slot value (<paramref name="slotValue"/>) and
|
||||
/// the output member path (<paramref name="outputMember"/> (for regular/non-boolean slots) for the slot.
|
||||
/// </summary>
|
||||
/// <param name="enforceNotNull">We need to ensure that _from variables are never null since view generation uses 2-valued boolean logic.
|
||||
/// If <paramref name="enforceNotNull"/>=true, the generated Cql adds a condition (AND <paramref name="slotValue"/> NOT NULL).
|
||||
/// This flag is used only for boolean slots.</param>
|
||||
internal SlotInfo(bool isRequiredByParent, bool isProjected, ProjectedSlot slotValue, MemberPath outputMember, bool enforceNotNull)
|
||||
{
|
||||
m_isRequiredByParent = isRequiredByParent;
|
||||
m_isProjected = isProjected;
|
||||
m_slotValue = slotValue;
|
||||
m_outputMember = outputMember;
|
||||
m_enforceNotNull = enforceNotNull;
|
||||
Debug.Assert(false == m_isRequiredByParent || m_slotValue != null, "Required slots cannot be null");
|
||||
Debug.Assert(m_slotValue is QualifiedSlot ||
|
||||
(m_slotValue == null && m_outputMember == null) || // unused boolean slot
|
||||
(m_slotValue is BooleanProjectedSlot) == (m_outputMember == null),
|
||||
"If slot is boolean slot, there is no member path for it and vice-versa");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// If slot is required by the parent. Can be reset to false in <see cref="ResetIsRequiredByParent"/> method.
|
||||
/// </summary>
|
||||
private bool m_isRequiredByParent;
|
||||
/// <summary>
|
||||
/// If the node is capable of projecting this slot.
|
||||
/// </summary>
|
||||
private readonly bool m_isProjected;
|
||||
/// <summary>
|
||||
/// The slot represented by this <see cref="SlotInfo"/>.
|
||||
/// </summary>
|
||||
private readonly ProjectedSlot m_slotValue;
|
||||
/// <summary>
|
||||
/// The output member path of this slot.
|
||||
/// </summary>
|
||||
private readonly MemberPath m_outputMember;
|
||||
/// <summary>
|
||||
/// Whether to add AND NOT NULL to Cql.
|
||||
/// </summary>
|
||||
private readonly bool m_enforceNotNull;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns true iff this slot is required by the <see cref="CqlBlock"/>'s parent.
|
||||
/// Can be reset to false by calling <see cref="ResetIsRequiredByParent"/> method.
|
||||
/// </summary>
|
||||
internal bool IsRequiredByParent
|
||||
{
|
||||
get { return m_isRequiredByParent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true iff this slot is projected by this <see cref="CqlBlock"/>.
|
||||
/// </summary>
|
||||
internal bool IsProjected
|
||||
{
|
||||
get { return m_isProjected; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the output memberpath of this slot
|
||||
/// </summary>
|
||||
internal MemberPath OutputMember
|
||||
{
|
||||
get { return m_outputMember; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the slot value corresponfing to this object.
|
||||
/// </summary>
|
||||
internal ProjectedSlot SlotValue
|
||||
{
|
||||
get { return m_slotValue; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Cql alias for this slot, e.g., "CPerson1_Pid", "_from0", etc
|
||||
/// </summary>
|
||||
internal string CqlFieldAlias
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_slotValue != null ? m_slotValue.GetCqlFieldAlias(m_outputMember) : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if Cql generated for the slot needs to have an extra AND IS NOT NULL condition.
|
||||
/// </summary>
|
||||
internal bool IsEnforcedNotNull
|
||||
{
|
||||
get { return m_enforceNotNull; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Sets the <see cref="IsRequiredByParent"/> to false.
|
||||
/// Note we don't have a setter because we don't want people to set this field to true after the object has been created.
|
||||
/// </summary>
|
||||
internal void ResetIsRequiredByParent()
|
||||
{
|
||||
m_isRequiredByParent = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates eSQL representation of the slot. For different slots, the result is different, e.g., "_from0", "CPerson1.pid", "TREAT(....)".
|
||||
/// </summary>
|
||||
internal StringBuilder AsEsql(StringBuilder builder, string blockAlias, int indentLevel)
|
||||
{
|
||||
if (m_enforceNotNull)
|
||||
{
|
||||
builder.Append('(');
|
||||
m_slotValue.AsEsql(builder, m_outputMember, blockAlias, indentLevel);
|
||||
builder.Append(" AND ");
|
||||
m_slotValue.AsEsql(builder, m_outputMember, blockAlias, indentLevel);
|
||||
builder.Append(" IS NOT NULL)");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_slotValue.AsEsql(builder, m_outputMember, blockAlias, indentLevel);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates CQT representation of the slot.
|
||||
/// </summary>
|
||||
internal DbExpression AsCqt(DbExpression row)
|
||||
{
|
||||
DbExpression cqt = m_slotValue.AsCqt(row, m_outputMember);
|
||||
if (m_enforceNotNull)
|
||||
{
|
||||
cqt = cqt.And(cqt.IsNull().Not());
|
||||
}
|
||||
return cqt;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
if (m_slotValue != null)
|
||||
{
|
||||
builder.Append(CqlFieldAlias);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="UnionCqlBlock.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.CqlGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents Union nodes in the <see cref="CqlBlock"/> tree.
|
||||
/// </summary>
|
||||
internal sealed class UnionCqlBlock : CqlBlock
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a union block with SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>), WHERE (true), AS (<paramref name="blockAliasNum"/>).
|
||||
/// </summary>
|
||||
internal UnionCqlBlock(SlotInfo[] slotInfos, List<CqlBlock> children, CqlIdentifiers identifiers, int blockAliasNum)
|
||||
: base(slotInfos, children, BoolExpression.True, identifiers, blockAliasNum)
|
||||
{ }
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, bool isTopLevel, int indentLevel)
|
||||
{
|
||||
Debug.Assert(this.Children.Count > 0, "UnionCqlBlock: Children collection must not be empty");
|
||||
|
||||
// Simply get the Cql versions of the children and add the union operator between them.
|
||||
bool isFirst = true;
|
||||
foreach (CqlBlock child in Children)
|
||||
{
|
||||
if (false == isFirst)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 1);
|
||||
builder.Append(OpCellTreeNode.OpToEsql(CellTreeOpType.Union));
|
||||
}
|
||||
isFirst = false;
|
||||
|
||||
builder.Append(" (");
|
||||
child.AsEsql(builder, isTopLevel, indentLevel + 1);
|
||||
builder.Append(')');
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(bool isTopLevel)
|
||||
{
|
||||
Debug.Assert(this.Children.Count > 0, "UnionCqlBlock: Children collection must not be empty");
|
||||
DbExpression cqt = this.Children[0].AsCqt(isTopLevel);
|
||||
for (int i = 1; i < this.Children.Count; ++i)
|
||||
{
|
||||
cqt = cqt.UnionAll(this.Children[i].AsCqt(isTopLevel));
|
||||
}
|
||||
return cqt;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user