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,298 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Linq;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
|
||||
// Resolves references to columns/expressions defined in other scopes
|
||||
internal class SqlResolver {
|
||||
Visitor visitor;
|
||||
|
||||
internal SqlResolver() {
|
||||
this.visitor = new Visitor();
|
||||
}
|
||||
|
||||
internal SqlNode Resolve(SqlNode node) {
|
||||
return this.visitor.Visit(node);
|
||||
}
|
||||
|
||||
private static string GetColumnName(SqlColumn c) {
|
||||
#if DEBUG
|
||||
return c.Text;
|
||||
#else
|
||||
return c.Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
class Visitor : SqlScopedVisitor {
|
||||
SqlBubbler bubbler;
|
||||
|
||||
internal Visitor() {
|
||||
this.bubbler = new SqlBubbler();
|
||||
}
|
||||
|
||||
internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
|
||||
SqlColumnRef result = this.BubbleUp(cref);
|
||||
if (result == null) {
|
||||
throw Error.ColumnReferencedIsNotInScope(GetColumnName(cref.Column));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SqlColumnRef BubbleUp(SqlColumnRef cref) {
|
||||
for (Scope s = this.CurrentScope; s != null; s = s.ContainingScope) {
|
||||
if (s.Source != null) {
|
||||
SqlColumn found = this.bubbler.BubbleUp(cref.Column, s.Source);
|
||||
if (found != null) {
|
||||
if (found != cref.Column)
|
||||
return new SqlColumnRef(found);
|
||||
return cref;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlScopedVisitor : SqlVisitor {
|
||||
internal Scope CurrentScope;
|
||||
|
||||
internal class Scope {
|
||||
SqlNode source;
|
||||
Scope containing;
|
||||
internal Scope(SqlNode source, Scope containing) {
|
||||
this.source = source;
|
||||
this.containing = containing;
|
||||
}
|
||||
internal SqlNode Source {
|
||||
get { return this.source; }
|
||||
}
|
||||
internal Scope ContainingScope {
|
||||
get { return this.containing; }
|
||||
}
|
||||
}
|
||||
|
||||
internal SqlScopedVisitor() {
|
||||
this.CurrentScope = new Scope(null, null);
|
||||
}
|
||||
|
||||
internal override SqlExpression VisitSubSelect(SqlSubSelect ss) {
|
||||
Scope save = this.CurrentScope;
|
||||
this.CurrentScope = new Scope(null, this.CurrentScope);
|
||||
base.VisitSubSelect(ss);
|
||||
this.CurrentScope = save;
|
||||
return ss;
|
||||
}
|
||||
|
||||
internal override SqlSelect VisitSelect(SqlSelect select) {
|
||||
select.From = (SqlSource)this.Visit(select.From);
|
||||
|
||||
Scope save = this.CurrentScope;
|
||||
this.CurrentScope = new Scope(select.From, this.CurrentScope.ContainingScope);
|
||||
|
||||
select.Where = this.VisitExpression(select.Where);
|
||||
for (int i = 0, n = select.GroupBy.Count; i < n; i++) {
|
||||
select.GroupBy[i] = this.VisitExpression(select.GroupBy[i]);
|
||||
}
|
||||
select.Having = this.VisitExpression(select.Having);
|
||||
for (int i = 0, n = select.OrderBy.Count; i < n; i++) {
|
||||
select.OrderBy[i].Expression = this.VisitExpression(select.OrderBy[i].Expression);
|
||||
}
|
||||
select.Top = this.VisitExpression(select.Top);
|
||||
select.Row = (SqlRow)this.Visit(select.Row);
|
||||
|
||||
// selection must be able to see its own projection
|
||||
this.CurrentScope = new Scope(select, this.CurrentScope.ContainingScope);
|
||||
select.Selection = this.VisitExpression(select.Selection);
|
||||
|
||||
this.CurrentScope = save;
|
||||
return select;
|
||||
}
|
||||
|
||||
internal override SqlStatement VisitInsert(SqlInsert sin) {
|
||||
Scope save = this.CurrentScope;
|
||||
this.CurrentScope = new Scope(sin, this.CurrentScope.ContainingScope);
|
||||
base.VisitInsert(sin);
|
||||
this.CurrentScope = save;
|
||||
return sin;
|
||||
}
|
||||
|
||||
internal override SqlStatement VisitUpdate(SqlUpdate sup) {
|
||||
Scope save = this.CurrentScope;
|
||||
this.CurrentScope = new Scope(sup.Select, this.CurrentScope.ContainingScope);
|
||||
base.VisitUpdate(sup);
|
||||
this.CurrentScope = save;
|
||||
return sup;
|
||||
}
|
||||
|
||||
internal override SqlStatement VisitDelete(SqlDelete sd) {
|
||||
Scope save = this.CurrentScope;
|
||||
this.CurrentScope = new Scope(sd, this.CurrentScope.ContainingScope);
|
||||
base.VisitDelete(sd);
|
||||
this.CurrentScope = save;
|
||||
return sd;
|
||||
}
|
||||
|
||||
internal override SqlSource VisitJoin(SqlJoin join) {
|
||||
Scope save = this.CurrentScope;
|
||||
switch (join.JoinType) {
|
||||
case SqlJoinType.CrossApply:
|
||||
case SqlJoinType.OuterApply: {
|
||||
this.Visit(join.Left);
|
||||
Scope tmp = new Scope(join.Left, this.CurrentScope.ContainingScope);
|
||||
this.CurrentScope = new Scope(null, tmp);
|
||||
this.Visit(join.Right);
|
||||
Scope tmp2 = new Scope(join.Right, tmp);
|
||||
this.CurrentScope = new Scope(null, tmp2);
|
||||
this.Visit(join.Condition);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.Visit(join.Left);
|
||||
this.Visit(join.Right);
|
||||
this.CurrentScope = new Scope(null, new Scope(join.Right, new Scope(join.Left, this.CurrentScope.ContainingScope)));
|
||||
this.Visit(join.Condition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.CurrentScope = save;
|
||||
return join;
|
||||
}
|
||||
}
|
||||
|
||||
// finds location of expression definition and re-projects that value all the
|
||||
// way to the outermost projection
|
||||
internal class SqlBubbler : SqlVisitor {
|
||||
SqlColumn match;
|
||||
SqlColumn found;
|
||||
|
||||
internal SqlBubbler() {
|
||||
}
|
||||
|
||||
internal SqlColumn BubbleUp(SqlColumn col, SqlNode source) {
|
||||
this.match = this.GetOriginatingColumn(col);
|
||||
this.found = null;
|
||||
this.Visit(source);
|
||||
return this.found;
|
||||
}
|
||||
|
||||
internal SqlColumn GetOriginatingColumn(SqlColumn col) {
|
||||
SqlColumnRef cref = col.Expression as SqlColumnRef;
|
||||
if (cref != null) {
|
||||
return this.GetOriginatingColumn(cref.Column);
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
internal override SqlRow VisitRow(SqlRow row) {
|
||||
foreach (SqlColumn c in row.Columns) {
|
||||
if (this.RefersToColumn(c, this.match)) {
|
||||
if (this.found != null) {
|
||||
throw Error.ColumnIsDefinedInMultiplePlaces(GetColumnName(this.match));
|
||||
}
|
||||
this.found = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
internal override SqlTable VisitTable(SqlTable tab) {
|
||||
foreach (SqlColumn c in tab.Columns) {
|
||||
if (c == this.match) {
|
||||
if (this.found != null)
|
||||
throw Error.ColumnIsDefinedInMultiplePlaces(GetColumnName(this.match));
|
||||
this.found = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
internal override SqlSource VisitJoin(SqlJoin join) {
|
||||
switch (join.JoinType) {
|
||||
case SqlJoinType.CrossApply:
|
||||
case SqlJoinType.OuterApply: {
|
||||
this.Visit(join.Left);
|
||||
if (this.found == null) {
|
||||
this.Visit(join.Right);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.Visit(join.Left);
|
||||
this.Visit(join.Right);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return join;
|
||||
}
|
||||
|
||||
internal override SqlExpression VisitTableValuedFunctionCall(SqlTableValuedFunctionCall fc) {
|
||||
foreach (SqlColumn c in fc.Columns) {
|
||||
if (c == this.match) {
|
||||
if (this.found != null)
|
||||
throw Error.ColumnIsDefinedInMultiplePlaces(GetColumnName(this.match));
|
||||
this.found = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fc;
|
||||
}
|
||||
|
||||
private void ForceLocal(SqlRow row, string name) {
|
||||
bool isLocal = false;
|
||||
// check to see if it already exists locally
|
||||
foreach (SqlColumn c in row.Columns) {
|
||||
if (this.RefersToColumn(c, this.found)) {
|
||||
this.found = c;
|
||||
isLocal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isLocal) {
|
||||
// need to put this in the local projection list to bubble it up
|
||||
SqlColumn c = new SqlColumn(found.ClrType, found.SqlType, name, this.found.MetaMember, new SqlColumnRef(this.found), row.SourceExpression);
|
||||
row.Columns.Add(c);
|
||||
this.found = c;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsFoundInGroup(SqlSelect select) {
|
||||
// does the column happen to be listed in the group-by clause?
|
||||
foreach (SqlExpression exp in select.GroupBy) {
|
||||
if (this.RefersToColumn(exp, this.found) || this.RefersToColumn(exp, this.match)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override SqlSelect VisitSelect(SqlSelect select) {
|
||||
// look in this projection
|
||||
this.Visit(select.Row);
|
||||
|
||||
if (this.found == null) {
|
||||
// look in upstream projections
|
||||
this.Visit(select.From);
|
||||
|
||||
// bubble it up
|
||||
if (this.found != null) {
|
||||
if (select.IsDistinct && !match.IsConstantColumn) {
|
||||
throw Error.ColumnIsNotAccessibleThroughDistinct(GetColumnName(this.match));
|
||||
}
|
||||
if (select.GroupBy.Count == 0 || this.IsFoundInGroup(select)) {
|
||||
this.ForceLocal(select.Row, this.found.Name);
|
||||
}
|
||||
else {
|
||||
// found it, but its hidden behind the group-by
|
||||
throw Error.ColumnIsNotAccessibleThroughGroupBy(GetColumnName(this.match));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return select;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user