/mcs/class/referencesource/System.Data.Linq/SqlClient/Query/SqlColumnizer.cs
C# | 185 lines | 161 code | 19 blank | 5 comment | 23 complexity | f7765ebc6a9d8604cd13a2aa1081db92 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0
- using System;
- using System.Collections.Generic;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Data.Linq;
- using System.Data.Linq.Mapping;
- using System.Data.Linq.Provider;
- using System.Linq;
- using System.Data.Linq.SqlClient;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics;
- namespace System.Data.Linq.SqlClient {
- // partions select expressions and common subexpressions into scalar and non-scalar pieces by
- // wrapping scalar pieces floating column nodes.
- internal class SqlColumnizer {
- ColumnNominator nominator;
- ColumnDeclarer declarer;
- internal SqlColumnizer() {
- this.nominator = new ColumnNominator();
- this.declarer = new ColumnDeclarer();
- }
- internal SqlExpression ColumnizeSelection(SqlExpression selection) {
- return this.declarer.Declare(selection, this.nominator.Nominate(selection));
- }
- internal static bool CanBeColumn(SqlExpression expression) {
- return ColumnNominator.CanBeColumn(expression);
- }
- class ColumnDeclarer : SqlVisitor {
- HashSet<SqlExpression> candidates;
- internal ColumnDeclarer() {
- }
- internal SqlExpression Declare(SqlExpression expression, HashSet<SqlExpression> candidates) {
- this.candidates = candidates;
- return (SqlExpression)this.Visit(expression);
- }
- internal override SqlNode Visit(SqlNode node) {
- SqlExpression expr = node as SqlExpression;
- if (expr != null) {
- if (this.candidates.Contains(expr)) {
- if (expr.NodeType == SqlNodeType.Column ||
- expr.NodeType == SqlNodeType.ColumnRef) {
- return expr;
- }
- else {
- return new SqlColumn(expr.ClrType, expr.SqlType, null, null, expr, expr.SourceExpression);
- }
- }
- }
- return base.Visit(node);
- }
- }
- class ColumnNominator : SqlVisitor {
- bool isBlocked;
- HashSet<SqlExpression> candidates;
- internal HashSet<SqlExpression> Nominate(SqlExpression expression) {
- this.candidates = new HashSet<SqlExpression>();
- this.isBlocked = false;
- this.Visit(expression);
- return this.candidates;
- }
- internal override SqlNode Visit(SqlNode node) {
- SqlExpression expression = node as SqlExpression;
- if (expression != null) {
- bool saveIsBlocked = this.isBlocked;
- this.isBlocked = false;
- if (CanRecurseColumnize(expression)) {
- base.Visit(expression);
- }
- if (!this.isBlocked) {
- if (CanBeColumn(expression)) {
- this.candidates.Add(expression);
- }
- else {
- this.isBlocked = true;
- }
- }
- this.isBlocked |= saveIsBlocked;
- }
- return node;
- }
- internal override SqlExpression VisitSimpleCase(SqlSimpleCase c) {
- c.Expression = this.VisitExpression(c.Expression);
- for (int i = 0, n = c.Whens.Count; i < n; i++) {
- // Don't walk down the match side. This can't be a column.
- c.Whens[i].Value = this.VisitExpression(c.Whens[i].Value);
- }
- return c;
- }
- internal override SqlExpression VisitTypeCase(SqlTypeCase tc) {
- tc.Discriminator = this.VisitExpression(tc.Discriminator);
- for (int i = 0, n = tc.Whens.Count; i < n; i++) {
- // Don't walk down the match side. This can't be a column.
- tc.Whens[i].TypeBinding = this.VisitExpression(tc.Whens[i].TypeBinding);
- }
- return tc;
- }
- internal override SqlExpression VisitClientCase(SqlClientCase c) {
- c.Expression = this.VisitExpression(c.Expression);
- for (int i = 0, n = c.Whens.Count; i < n; i++) {
- // Don't walk down the match side. This can't be a column.
- c.Whens[i].Value = this.VisitExpression(c.Whens[i].Value);
- }
- return c;
- }
- private static bool CanRecurseColumnize(SqlExpression expr) {
- switch (expr.NodeType) {
- case SqlNodeType.AliasRef:
- case SqlNodeType.ColumnRef:
- case SqlNodeType.Column:
- case SqlNodeType.Multiset:
- case SqlNodeType.Element:
- case SqlNodeType.ScalarSubSelect:
- case SqlNodeType.Exists:
- case SqlNodeType.ClientQuery:
- case SqlNodeType.SharedExpressionRef:
- case SqlNodeType.Link:
- case SqlNodeType.Nop:
- case SqlNodeType.Value:
- case SqlNodeType.Select:
- return false;
- default:
- return true;
- }
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", 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.")]
- private static bool IsClientOnly(SqlExpression expr) {
- switch (expr.NodeType) {
- case SqlNodeType.ClientCase:
- case SqlNodeType.TypeCase:
- case SqlNodeType.ClientArray:
- case SqlNodeType.Grouping:
- case SqlNodeType.DiscriminatedType:
- case SqlNodeType.SharedExpression:
- case SqlNodeType.SimpleExpression:
- case SqlNodeType.AliasRef:
- case SqlNodeType.Multiset:
- case SqlNodeType.Element:
- case SqlNodeType.ClientQuery:
- case SqlNodeType.SharedExpressionRef:
- case SqlNodeType.Link:
- case SqlNodeType.Nop:
- return true;
- case SqlNodeType.OuterJoinedValue:
- return IsClientOnly(((SqlUnary)expr).Operand);
- default:
- return false;
- }
- }
- internal static bool CanBeColumn(SqlExpression expression) {
- if (!IsClientOnly(expression)
- && expression.NodeType != SqlNodeType.Column
- && expression.SqlType.CanBeColumn) {
- switch (expression.NodeType) {
- case SqlNodeType.MethodCall:
- case SqlNodeType.Member:
- case SqlNodeType.New:
- return PostBindDotNetConverter.CanConvert(expression);
- default:
- return true;
- }
- }
- return false;
- }
- }
- }
- }