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,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Data.Linq.SqlClient {
|
||||
using System.Data.Linq.Mapping;
|
||||
using System.Data.Linq.Provider;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
// moves order-by clauses from sub-queries to outer-most or top selects
|
||||
// removes ordering in correlated sub-queries
|
||||
internal class SqlReorderer {
|
||||
TypeSystemProvider typeProvider;
|
||||
SqlFactory sql;
|
||||
|
||||
internal SqlReorderer(TypeSystemProvider typeProvider, SqlFactory sqlFactory) {
|
||||
this.typeProvider = typeProvider;
|
||||
this.sql = sqlFactory;
|
||||
}
|
||||
|
||||
internal SqlNode Reorder(SqlNode node) {
|
||||
return new Visitor(this.typeProvider, this.sql).Visit(node);
|
||||
}
|
||||
|
||||
class Visitor : SqlVisitor {
|
||||
TypeSystemProvider typeProvider;
|
||||
bool topSelect = true;
|
||||
bool addPrimaryKeys;
|
||||
List<SqlOrderExpression> orders;
|
||||
List<SqlOrderExpression> rowNumberOrders;
|
||||
SqlSelect currentSelect;
|
||||
SqlFactory sql;
|
||||
SqlAggregateChecker aggregateChecker;
|
||||
|
||||
internal Visitor(TypeSystemProvider typeProvider, SqlFactory sqlFactory) {
|
||||
this.orders = new List<SqlOrderExpression>();
|
||||
this.typeProvider = typeProvider;
|
||||
this.sql = sqlFactory;
|
||||
this.aggregateChecker = new SqlAggregateChecker();
|
||||
}
|
||||
|
||||
internal override SqlExpression VisitSubSelect(SqlSubSelect ss) {
|
||||
List<SqlOrderExpression> save = this.orders;
|
||||
this.orders = new List<SqlOrderExpression>();
|
||||
base.VisitSubSelect(ss);
|
||||
this.orders = save;
|
||||
return ss;
|
||||
}
|
||||
|
||||
private void PrependOrderExpressions(IEnumerable<SqlOrderExpression> exprs) {
|
||||
if (exprs != null) {
|
||||
this.Orders.InsertRange(0, exprs);
|
||||
}
|
||||
}
|
||||
|
||||
private List<SqlOrderExpression> Orders {
|
||||
get {
|
||||
if (this.orders == null) {
|
||||
this.orders = new List<SqlOrderExpression>();
|
||||
}
|
||||
return this.orders;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal override SqlSource VisitJoin(SqlJoin join) {
|
||||
this.Visit(join.Left);
|
||||
List<SqlOrderExpression> leftOrders = this.orders;
|
||||
this.orders = null;
|
||||
this.Visit(join.Right);
|
||||
this.PrependOrderExpressions(leftOrders);
|
||||
return join;
|
||||
}
|
||||
|
||||
internal override SqlNode VisitUnion(SqlUnion su) {
|
||||
// ordering does not carry through a union
|
||||
this.orders = null;
|
||||
su.Left = this.Visit(su.Left);
|
||||
this.orders = null;
|
||||
su.Right = this.Visit(su.Right);
|
||||
this.orders = null;
|
||||
return su;
|
||||
}
|
||||
|
||||
internal override SqlAlias VisitAlias(SqlAlias a) {
|
||||
|
||||
SqlTable tab = a.Node as SqlTable;
|
||||
SqlTableValuedFunctionCall tvf = a.Node as SqlTableValuedFunctionCall;
|
||||
|
||||
if (this.addPrimaryKeys && (tab != null || tvf != null)) {
|
||||
List<SqlOrderExpression> list = new List<SqlOrderExpression>();
|
||||
|
||||
bool isTable = tab != null;
|
||||
MetaType rowType = isTable ? tab.RowType : tvf.RowType;
|
||||
foreach (MetaDataMember mm in rowType.IdentityMembers) {
|
||||
string name = mm.MappedName;
|
||||
SqlColumn col;
|
||||
Expression sourceExpression;
|
||||
List<SqlColumn> columns;
|
||||
|
||||
if (isTable) {
|
||||
col = tab.Find(name);
|
||||
sourceExpression = tab.SourceExpression;
|
||||
columns = tab.Columns;
|
||||
}
|
||||
else {
|
||||
col = tvf.Find(name);
|
||||
sourceExpression = tvf.SourceExpression;
|
||||
columns = tvf.Columns;
|
||||
}
|
||||
|
||||
if (col == null) {
|
||||
col = new SqlColumn(mm.MemberAccessor.Type, typeProvider.From(mm.MemberAccessor.Type), name, mm, null, sourceExpression);
|
||||
col.Alias = a;
|
||||
columns.Add(col);
|
||||
}
|
||||
list.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col)));
|
||||
}
|
||||
|
||||
this.PrependOrderExpressions(list);
|
||||
|
||||
return a;
|
||||
}
|
||||
else {
|
||||
return base.VisitAlias(a);
|
||||
}
|
||||
}
|
||||
|
||||
internal override SqlSelect VisitSelect(SqlSelect select) {
|
||||
bool saveTop = this.topSelect;
|
||||
bool savePK = this.addPrimaryKeys;
|
||||
|
||||
SqlSelect saveSelect = this.currentSelect;
|
||||
this.currentSelect = select;
|
||||
|
||||
if (select.OrderingType == SqlOrderingType.Always) {
|
||||
this.addPrimaryKeys = true;
|
||||
}
|
||||
|
||||
this.topSelect = false;
|
||||
|
||||
// can't forward ordering information through a group-by
|
||||
if (select.GroupBy.Count > 0) {
|
||||
this.Visit(select.From);
|
||||
this.orders = null;
|
||||
}
|
||||
else {
|
||||
this.Visit(select.From);
|
||||
}
|
||||
|
||||
if (select.OrderBy.Count > 0) {
|
||||
this.PrependOrderExpressions(select.OrderBy);
|
||||
}
|
||||
|
||||
List<SqlOrderExpression> save = this.orders;
|
||||
this.orders = null;
|
||||
this.rowNumberOrders = save; // lest orders be null when we need info
|
||||
|
||||
/* do all the lower level stuff */
|
||||
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.Selection = this.VisitExpression(select.Selection);
|
||||
select.Row = (SqlRow)this.Visit(select.Row);
|
||||
|
||||
this.topSelect = saveTop;
|
||||
this.addPrimaryKeys = savePK;
|
||||
|
||||
this.orders = save;
|
||||
|
||||
// all ordering is blocked for this layer and above
|
||||
if (select.OrderingType == SqlOrderingType.Blocked) {
|
||||
this.orders = null;
|
||||
}
|
||||
|
||||
// rebuild orderby expressions, provided this select doesn't contain a SqlRowNumber
|
||||
// otherwise, replace the orderby with a reference to that column
|
||||
select.OrderBy.Clear();
|
||||
var rowNumberChecker = new SqlRowNumberChecker();
|
||||
|
||||
if (rowNumberChecker.HasRowNumber(select) && rowNumberChecker.RowNumberColumn != null) {
|
||||
select.Row.Columns.Remove(rowNumberChecker.RowNumberColumn);
|
||||
this.PushDown(rowNumberChecker.RowNumberColumn);
|
||||
this.Orders.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(rowNumberChecker.RowNumberColumn)));
|
||||
}
|
||||
if ((this.topSelect || select.Top != null) && select.OrderingType != SqlOrderingType.Never && this.orders != null) {
|
||||
this.orders = new HashSet<SqlOrderExpression>(this.orders).ToList();
|
||||
SqlDuplicator dup = new SqlDuplicator(true);
|
||||
foreach (SqlOrderExpression sox in this.orders) {
|
||||
select.OrderBy.Add(new SqlOrderExpression(sox.OrderType, (SqlExpression)dup.Duplicate(sox.Expression)));
|
||||
}
|
||||
}
|
||||
this.currentSelect = saveSelect;
|
||||
|
||||
return select;
|
||||
}
|
||||
|
||||
internal override SqlRowNumber VisitRowNumber(SqlRowNumber rowNumber) {
|
||||
if (rowNumber.OrderBy.Count > 0) return rowNumber;
|
||||
|
||||
SqlDuplicator dup = new SqlDuplicator(true);
|
||||
List<SqlOrderExpression> orderBy = new List<SqlOrderExpression>();
|
||||
List<SqlOrderExpression> existingOrders = new List<SqlOrderExpression>();
|
||||
|
||||
if (this.rowNumberOrders != null && this.rowNumberOrders.Count != 0) {
|
||||
existingOrders = new List<SqlOrderExpression>(this.rowNumberOrders);
|
||||
}
|
||||
else if (this.orders != null) {
|
||||
existingOrders = new List<SqlOrderExpression>(this.orders);
|
||||
}
|
||||
|
||||
foreach (SqlOrderExpression expr in existingOrders) {
|
||||
if (!expr.Expression.IsConstantColumn) {
|
||||
orderBy.Add(expr);
|
||||
if (this.rowNumberOrders != null) {
|
||||
this.rowNumberOrders.Remove(expr);
|
||||
}
|
||||
if (this.orders != null) {
|
||||
this.orders.Remove(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowNumber.OrderBy.Clear();
|
||||
|
||||
if (orderBy.Count == 0) {
|
||||
List<SqlColumn> columns = SqlGatherColumnsProduced.GatherColumns(this.currentSelect.From);
|
||||
|
||||
foreach (SqlColumn col in columns) {
|
||||
if (col.Expression.SqlType.IsOrderable) {
|
||||
orderBy.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col)));
|
||||
}
|
||||
}
|
||||
|
||||
if (orderBy.Count == 0) {
|
||||
// insert simple column
|
||||
SqlColumn col =
|
||||
new SqlColumn(
|
||||
"rowNumberOrder",
|
||||
sql.Value(typeof(int), this.typeProvider.From(typeof(int)), 1, false, rowNumber.SourceExpression)
|
||||
);
|
||||
this.PushDown(col);
|
||||
orderBy.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SqlOrderExpression sox in orderBy) {
|
||||
rowNumber.OrderBy.Add(new SqlOrderExpression(sox.OrderType, (SqlExpression)dup.Duplicate(sox.Expression)));
|
||||
}
|
||||
|
||||
return rowNumber;
|
||||
}
|
||||
|
||||
private void PushDown(SqlColumn column) {
|
||||
SqlSelect select = new SqlSelect(new SqlNop(column.ClrType, column.SqlType, column.SourceExpression), this.currentSelect.From, this.currentSelect.SourceExpression);
|
||||
this.currentSelect.From = new SqlAlias(select);
|
||||
select.Row.Columns.Add(column);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SqlGatherColumnsProduced {
|
||||
static internal List<SqlColumn> GatherColumns(SqlSource source) {
|
||||
List<SqlColumn> columns = new List<SqlColumn>();
|
||||
new Visitor(columns).Visit(source);
|
||||
return columns;
|
||||
}
|
||||
class Visitor : SqlVisitor {
|
||||
List<SqlColumn> columns;
|
||||
internal Visitor(List<SqlColumn> columns) {
|
||||
this.columns = columns;
|
||||
}
|
||||
internal override SqlSelect VisitSelect(SqlSelect select) {
|
||||
foreach (SqlColumn c in select.Row.Columns) {
|
||||
this.columns.Add(c);
|
||||
}
|
||||
return select;
|
||||
}
|
||||
internal override SqlNode VisitUnion(SqlUnion su) {
|
||||
return su;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user