|
using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Data.Linq.Provider;
using System.Diagnostics.CodeAnalysis;
namespace System.Data.Linq.SqlClient {
internal class SqlDuplicator {
DuplicatingVisitor superDuper;
internal SqlDuplicator()
: this(true) {
}
internal SqlDuplicator(bool ignoreExternalRefs) {
this.superDuper = new DuplicatingVisitor(ignoreExternalRefs);
}
internal static SqlNode Copy(SqlNode node) {
if (node == null)
return null;
switch (node.NodeType) {
case SqlNodeType.ColumnRef:
case SqlNodeType.Value:
case SqlNodeType.Parameter:
case SqlNodeType.Variable:
return node;
default:
return new SqlDuplicator().Duplicate(node);
}
}
internal SqlNode Duplicate(SqlNode node) {
return this.superDuper.Visit(node);
}
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
internal class DuplicatingVisitor : SqlVisitor {
Dictionary<SqlNode, SqlNode> nodeMap;
bool ingoreExternalRefs;
internal DuplicatingVisitor(bool ignoreExternalRefs) {
this.ingoreExternalRefs = ignoreExternalRefs;
this.nodeMap = new Dictionary<SqlNode, SqlNode>();
}
internal override SqlNode Visit(SqlNode node) {
if (node == null) {
return null;
}
SqlNode result = null;
if (this.nodeMap.TryGetValue(node, out result)) {
return result;
}
result = base.Visit(node);
this.nodeMap[node] = result;
return result;
}
internal override SqlExpression VisitDoNotVisit(SqlDoNotVisitExpression expr) {
// duplicator can duplicate through a do-no-visit node
return new SqlDoNotVisitExpression(this.VisitExpression(expr.Expression));
}
internal override SqlAlias VisitAlias(SqlAlias a) {
SqlAlias n = new SqlAlias(a.Node);
this.nodeMap[a] = n;
n.Node = this.Visit(a.Node);
n.Name = a.Name;
return n;
}
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
if (this.ingoreExternalRefs && !this.nodeMap.ContainsKey(aref.Alias)) {
return aref;
}
return new SqlAliasRef((SqlAlias)this.Visit(aref.Alias));
}
internal override SqlRowNumber VisitRowNumber(SqlRowNumber rowNumber) {
List<SqlOrderExpression> orderBy = new List<SqlOrderExpression>();
foreach (SqlOrderExpression expr in rowNumber.OrderBy) {
orderBy.Add(new SqlOrderExpression(expr.OrderType, (SqlExpression)this.Visit(expr.Expression)));
}
return new SqlRowNumber(rowNumber.ClrType, rowNumber.SqlType, orderBy, rowNumber.SourceExpression);
}
internal override SqlExpression VisitBinaryOperator(SqlBinary bo) {
SqlExpression left = (SqlExpression)this.Visit(bo.Left);
SqlExpression right = (SqlExpression)this.Visit(bo.Right);
return new SqlBinary(bo.NodeType, bo.ClrType, bo.SqlType, left, right, bo.Method);
}
internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
SqlSubSelect query = (SqlSubSelect) this.VisitExpression(cq.Query);
SqlClientQuery nq = new SqlClientQuery(query);
for (int i = 0, n = cq.Arguments.Count; i < n; i++) {
nq.Arguments.Add(this.VisitExpression(cq.Arguments[i]));
}
for (int i = 0, n = cq.Parameters.Count; i < n; i++) {
nq.Parameters.Add((SqlParameter)this.VisitExpression(cq.Parameters[i]));
}
return nq;
}
internal override SqlExpression VisitJoinedCollection(SqlJoinedCollection jc) {
return new SqlJoinedCollection(jc.ClrType, jc.SqlType, this.VisitExpression(jc.Expression), this.VisitExpression(jc.Count), jc.SourceExpression);
}
internal override SqlExpression VisitClientArray(SqlClientArray scar) {
SqlExpression[] exprs = new SqlExpression[scar.Expressions.Count];
for (int i = 0, n = exprs.Length; i < n; i++) {
exprs[i] = this.VisitExpression(scar.Expressions[i]);
}
return new SqlClientArray(scar.ClrType, scar.SqlType, exprs, scar.SourceExpression);
}
internal override SqlExpression VisitTypeCase(SqlTypeCase tc) {
SqlExpression disc = VisitExpression(tc.Discriminator);
List<SqlTypeCaseWhen> whens = new List<SqlTypeCaseWhen>();
foreach(SqlTypeCaseWhen when in tc.Whens) {
whens.Add(new SqlTypeCaseWhen(VisitExpression(when.Match), VisitExpression(when.TypeBinding)));
}
return new SqlTypeCase(tc.ClrType, tc.SqlType, tc.RowType, disc, whens, tc.SourceExpression);
}
internal override SqlExpression VisitNew(SqlNew sox) {
SqlExpression[] args = new SqlExpression[sox.Args.Count];
SqlMemberAssign[] bindings = new SqlMemberAssign[sox.Members.Count];
for (int i = 0, n = args.Length; i < n; i++) {
args[i] = this.VisitExpression(sox.Args[i]);
}
for (int i = 0, n = bindings.Length; i < n; i++) {
bindings[i] = this.VisitMemberAssign(sox.Members[i]);
}
return new SqlNew(sox.MetaType, sox.SqlType, sox.Constructor, args, sox.ArgMembers, bindings, sox.SourceExpression);
}
internal override SqlNode VisitLink(SqlLink link) {
SqlExpression[] exprs = new SqlExpression[link.KeyExpressions.Count];
for (int i = 0, n = exprs.Length; i < n; i++) {
exprs[i] = this.VisitExpression(link.KeyExpressions[i]);
}
SqlLink newLink = new SqlLink(new object(), link.RowType, link.ClrType, link.SqlType, null, link.Member, exprs, null, link.SourceExpression);
this.nodeMap[link] = newLink;
// break the potential cyclic tree by visiting these after adding to the map
newLink.Expression = this.VisitExpression(link.Expression);
newLink.Expansion = this.VisitExpression(link.Expansion);
return newLink;
}
internal override SqlExpression VisitColumn(SqlColumn col) {
SqlColumn n = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression);
this.nodeMap[col] = n;
n.Expression = this.VisitExpression(col.Expression);
n.Alias = (SqlAlias)this.Visit(col.Alias);
return n;
}
internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
if (this.ingoreExternalRefs && !this.nodeMap.ContainsKey(cref.Column)) {
return cref;
}
return new SqlColumnRef((SqlColumn)this.Visit(cref.Column));
}
internal override SqlStatement VisitDelete(SqlDelete sd) {
return new SqlDelete((SqlSelect)this.Visit(sd.Select), sd.SourceExpression);
}
internal override SqlExpression VisitElement(SqlSubSelect elem) {
return this.VisitMultiset(elem);
}
internal override SqlExpression VisitExists(SqlSubSelect sqlExpr) {
return new SqlSubSelect(sqlExpr.NodeType, sqlExpr.ClrType, sqlExpr.SqlType, (SqlSelect)this.Visit(sqlExpr.Select));
}
internal override SqlStatement VisitInsert(SqlInsert si) {
SqlInsert n = new SqlInsert(si.Table, this.VisitExpression(si.Expression), si.SourceExpression);
n.OutputKey = si.OutputKey;
n.OutputToLocal = si.OutputToLocal;
n.Row = this.VisitRow(si.Row);
return n;
}
internal override SqlSource VisitJoin(SqlJoin join) {
SqlSource left = this.VisitSource(join.Left);
SqlSource right = this.VisitSource(join.Right);
SqlExpression cond = (SqlExpression)this.Visit(join.Condition);
return new SqlJoin(join.JoinType, left, right, cond, join.SourceExpression);
}
internal override SqlExpression VisitValue(SqlValue value) {
return value;
}
internal override SqlNode VisitMember(SqlMember m) {
return new SqlMember(m.ClrType, m.SqlType, (SqlExpression)this.Visit(m.Expression), m.Member);
}
internal override SqlMemberAssign VisitMemberAssign(SqlMemberAssign ma) {
return new SqlMemberAssign(ma.Member, (SqlExpression)this.Visit(ma.Expression));
}
internal override SqlExpression VisitMultiset(SqlSubSelect sms) {
return new SqlSubSelect(sms.NodeType, sms.ClrType, sms.SqlType, (SqlSelect)this.Visit(sms.Select));
}
internal override SqlExpression VisitParameter(SqlParameter p) {
SqlParameter n = new SqlParameter(p.ClrType, p.SqlType, p.Name, p.SourceExpression);
n.Direction = p.Direction;
return n;
}
internal override SqlRow VisitRow(SqlRow row) {
SqlRow nrow = new SqlRow(row.SourceExpression);
foreach (SqlColumn c in row.Columns) {
nrow.Columns.Add((SqlColumn)this.Visit(c));
}
return nrow;
}
internal override SqlExpression VisitScalarSubSelect(SqlSubSelect ss) {
return new SqlSubSelect(SqlNodeType.ScalarSubSelect, ss.ClrType, ss.SqlType, this.VisitSequence(ss.Select));
}
internal override SqlSelect VisitSelect(SqlSelect select) {
SqlSource from = this.VisitSource(select.From);
List<SqlExpression> gex = null;
if (select.GroupBy.Count > 0) {
gex = new List<SqlExpression>(select.GroupBy.Count);
foreach (SqlExpression sqlExpr in select.GroupBy) {
gex.Add((SqlExpression)this.Visit(sqlExpr));
}
}
SqlExpression having = (SqlExpression)this.Visit(select.Having);
List<SqlOrderExpression> lex = null;
if (select.OrderBy.Count > 0) {
lex = new List<SqlOrderExpression>(select.OrderBy.Count);
foreach (SqlOrderExpression sox in select.OrderBy) {
SqlOrderExpression nsox = new SqlOrderExpression(sox.OrderType, (SqlExpression)this.Visit(sox.Expression));
lex.Add(nsox);
}
}
SqlExpression top = (SqlExpression)this.Visit(select.Top);
SqlExpression where = (SqlExpression)this.Visit(select.Where);
SqlRow row = (SqlRow)this.Visit(select.Row);
SqlExpression selection = this.VisitExpression(select.Selection);
SqlSelect n = new SqlSelect(selection, from, select.SourceExpression);
if (gex != null)
n.GroupBy.AddRange(gex);
n.Having = having;
if (lex != null)
n.OrderBy.AddRange(lex);
n.OrderingType = select.OrderingType;
n.Row = row;
n.Top = top;
n.IsDistinct = select.IsDistinct;
n.IsPercent = select.IsPercent;
n.Where = where;
n.DoNotOutput = select.DoNotOutput;
return n;
}
internal override SqlTable VisitTable(SqlTable tab) {
SqlTable nt = new SqlTable(tab.MetaTable, tab.RowType, tab.SqlRowType, tab.SourceExpression);
this.nodeMap[tab] = nt;
foreach (SqlColumn c in tab.Columns) {
nt.Columns.Add((SqlColumn)this.Visit(c));
}
return nt;
}
internal override SqlUserQuery VisitUserQuery(SqlUserQuery suq) {
List<SqlExpression> args = new List<SqlExpression>(suq.Arguments.Count);
foreach (SqlExpression expr in suq.Arguments) {
args.Add(this.VisitExpression(expr));
}
SqlExpression projection = this.VisitExpression(suq.Projection);
SqlUserQuery n = new SqlUserQuery(suq.QueryText, projection, args, suq.SourceExpression);
this.nodeMap[suq] = n;
foreach (SqlUserColumn suc in suq.Columns) {
SqlUserColumn dupSuc = new SqlUserColumn(suc.ClrType, suc.SqlType, suc.Query, suc.Name, suc.IsRequired, suc.SourceExpression);
this.nodeMap[suc] = dupSuc;
n.Columns.Add(dupSuc);
}
return n;
}
internal override SqlStoredProcedureCall VisitStoredProcedureCall(SqlStoredProcedureCall spc) {
List<SqlExpression> args = new List<SqlExpression>(spc.Arguments.Count);
foreach (SqlExpression expr in spc.Arguments) {
args.Add(this.VisitExpression(expr));
}
SqlExpression projection = this.VisitExpression(spc.Projection);
SqlStoredProcedureCall n = new SqlStoredProcedureCall(spc.Function, projection, args, spc.SourceExpression);
this.nodeMap[spc] = n;
foreach (SqlUserColumn suc in spc.Columns) {
n.Columns.Add((SqlUserColumn)this.Visit(suc));
}
return n;
}
internal override SqlExpression VisitUserColumn(SqlUserColumn suc) {
if (this.ingoreExternalRefs && !this.nodeMap.ContainsKey(suc)) {
return suc;
}
return new SqlUserColumn(suc.ClrType, suc.SqlType, suc.Query, suc.Name, suc.IsRequired, suc.SourceExpression);
}
internal override SqlExpression VisitUserRow(SqlUserRow row) {
return new SqlUserRow(row.RowType, row.SqlType, (SqlUserQuery)this.Visit(row.Query), row.SourceExpression);
}
internal override SqlExpression VisitTreat(SqlUnary t) {
return new SqlUnary(SqlNodeType.Treat, t.ClrType, t.SqlType, (SqlExpression)this.Visit(t.Operand), t.SourceExpression);
}
internal override SqlExpression VisitUnaryOperator(SqlUnary uo) {
return new SqlUnary(uo.NodeType, uo.ClrType, uo.SqlType, (SqlExpression)this.Visit(uo.Operand), uo.Method, uo.SourceExpression);
}
internal override SqlStatement VisitUpdate(SqlUpdate su) {
SqlSelect ss = (SqlSelect)this.Visit(su.Select);
List<SqlAssign> assignments = new List<SqlAssign>(su.Assignments.Count);
foreach (SqlAssign sa in su.Assignments) {
assignments.Add((SqlAssign)this.Visit(sa));
}
return new SqlUpdate(ss, assignments, su.SourceExpression);
}
internal override SqlStatement VisitAssign(SqlAssign sa) {
return new SqlAssign(this.VisitExpression(sa.LValue), this.VisitExpression(sa.RValue), sa.SourceExpression);
}
internal override SqlExpression VisitSearchedCase(SqlSearchedCase c) {
SqlExpression @else = this.VisitExpression(c.Else);
SqlWhen[] whens = new SqlWhen[c.Whens.Count];
for (int i = 0, n = whens.Length; i < n; i++) {
SqlWhen when = c.Whens[i];
whens[i] = new SqlWhen(this.VisitExpression(when.Match), this.VisitExpression(when.Value));
}
return new SqlSearchedCase(c.ClrType, whens, @else, c.SourceExpression);
}
internal override SqlExpression VisitClientCase(SqlClientCase c) {
SqlExpression expr = this.VisitExpression(c.Expression);
SqlClientWhen[] whens = new SqlClientWhen[c.Whens.Count];
for (int i = 0, n = whens.Length; i < n; i++) {
SqlClientWhen when = c.Whens[i];
whens[i] = new SqlClientWhen(this.VisitExpression(when.Match), this.VisitExpression(when.Value));
}
return new SqlClientCase(c.ClrType, expr, whens, c.SourceExpression);
}
internal override SqlExpression VisitSimpleCase(SqlSimpleCase c) {
SqlExpression expr = this.VisitExpression(c.Expression);
SqlWhen[] whens = new SqlWhen[c.Whens.Count];
for (int i = 0, n = whens.Length; i < n; i++) {
SqlWhen when = c.Whens[i];
whens[i] = new SqlWhen(this.VisitExpression(when.Match), this.VisitExpression(when.Value));
}
return new SqlSimpleCase(c.ClrType, expr, whens, c.SourceExpression);
}
internal override SqlNode VisitUnion(SqlUnion su) {
return new SqlUnion(this.Visit(su.Left), this.Visit(su.Right), su.All);
}
internal override SqlExpression VisitExprSet(SqlExprSet xs) {
SqlExpression[] exprs = new SqlExpression[xs.Expressions.Count];
for (int i = 0, n = exprs.Length; i < n; i++) {
exprs[i] = this.VisitExpression(xs.Expressions[i]);
}
return new SqlExprSet(xs.ClrType, exprs, xs.SourceExpression);
}
internal override SqlBlock VisitBlock(SqlBlock block) {
SqlBlock nb = new SqlBlock(block.SourceExpression);
foreach (SqlStatement stmt in block.Statements) {
nb.Statements.Add((SqlStatement)this.Visit(stmt));
}
return nb;
}
internal override SqlExpression VisitVariable(SqlVariable v) {
return v;
}
internal override SqlExpression VisitOptionalValue(SqlOptionalValue sov) {
SqlExpression hasValue = this.VisitExpression(sov.HasValue);
SqlExpression value = this.VisitExpression(sov.Value);
return new SqlOptionalValue(hasValue, value);
}
internal override SqlExpression VisitBetween(SqlBetween between) {
SqlBetween nbet = new SqlBetween(
between.ClrType,
between.SqlType,
this.VisitExpression(between.Expression),
this.VisitExpression(between.Start),
this.VisitExpression(between.End),
between.SourceExpression
);
return nbet;
}
internal override SqlExpression VisitIn(SqlIn sin) {
SqlIn nin = new SqlIn(sin.ClrType, sin.SqlType, this.VisitExpression(sin.Expression), sin.Values, sin.SourceExpression);
for (int i = 0, n = nin.Values.Count; i < n; i++) {
nin.Values[i] = this.VisitExpression(nin.Values[i]);
}
return nin;
}
internal override SqlExpression VisitLike(SqlLike like) {
return new SqlLike(
like.ClrType, like.SqlType,
this.VisitExpression(like.Expression),
this.VisitExpression(like.Pattern),
this.VisitExpression(like.Escape),
like.SourceExpression
);
}
internal override SqlExpression VisitFunctionCall(SqlFunctionCall fc) {
SqlExpression[] args = new SqlExpression[fc.Arguments.Count];
for (int i = 0, n = fc.Arguments.Count; i < n; i++) {
args[i] = this.VisitExpression(fc.Arguments[i]);
}
return new SqlFunctionCall(fc.ClrType, fc.SqlType, fc.Name, args, fc.SourceExpression);
}
internal override SqlExpression VisitTableValuedFunctionCall(SqlTableValuedFunctionCall fc) {
SqlExpression[] args = new SqlExpression[fc.Arguments.Count];
for (int i = 0, n = fc.Arguments.Count; i < n; i++) {
args[i] = this.VisitExpression(fc.Arguments[i]);
}
SqlTableValuedFunctionCall nfc = new SqlTableValuedFunctionCall(fc.RowType, fc.ClrType, fc.SqlType, fc.Name, args, fc.SourceExpression);
this.nodeMap[fc] = nfc;
foreach (SqlColumn c in fc.Columns) {
nfc.Columns.Add((SqlColumn)this.Visit(c));
}
return nfc;
}
internal override SqlExpression VisitMethodCall(SqlMethodCall mc) {
SqlExpression[] args = new SqlExpression[mc.Arguments.Count];
for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
args[i] = this.VisitExpression(mc.Arguments[i]);
}
return new SqlMethodCall(mc.ClrType, mc.SqlType, mc.Method, this.VisitExpression(mc.Object), args, mc.SourceExpression);
}
internal override SqlExpression VisitSharedExpression(SqlSharedExpression sub) {
SqlSharedExpression n = new SqlSharedExpression(sub.Expression);
this.nodeMap[sub] = n;
n.Expression = this.VisitExpression(sub.Expression);
return n;
}
internal override SqlExpression VisitSharedExpressionRef(SqlSharedExpressionRef sref) {
if (this.ingoreExternalRefs && !this.nodeMap.ContainsKey(sref.SharedExpression)) {
return sref;
}
return new SqlSharedExpressionRef((SqlSharedExpression)this.Visit(sref.SharedExpression));
}
internal override SqlExpression VisitSimpleExpression(SqlSimpleExpression simple) {
SqlSimpleExpression n = new SqlSimpleExpression(this.VisitExpression(simple.Expression));
return n;
}
internal override SqlExpression VisitGrouping(SqlGrouping g) {
SqlGrouping n = new SqlGrouping(g.ClrType, g.SqlType,
this.VisitExpression(g.Key), this.VisitExpression(g.Group), g.SourceExpression
);
return n;
}
internal override SqlExpression VisitDiscriminatedType(SqlDiscriminatedType dt) {
return new SqlDiscriminatedType(dt.SqlType, this.VisitExpression(dt.Discriminator), dt.TargetType, dt.SourceExpression);
}
internal override SqlExpression VisitLift(SqlLift lift) {
return new SqlLift(lift.ClrType, this.VisitExpression(lift.Expression), lift.SourceExpression);
}
internal override SqlExpression VisitDiscriminatorOf(SqlDiscriminatorOf dof) {
return new SqlDiscriminatorOf(this.VisitExpression(dof.Object), dof.ClrType, dof.SqlType, dof.SourceExpression);
}
internal override SqlNode VisitIncludeScope(SqlIncludeScope scope) {
return new SqlIncludeScope(this.Visit(scope.Child), scope.SourceExpression);
}
}
}
}
|