//--------------------------------------------------------------------- // <copyright file="AbstractExpressions.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- using System; using System.Collections.Generic; using System.ComponentModel; using System.Data.Spatial; using System.Diagnostics; using System.Globalization; using System.Data.Metadata.Edm; using System.Data.Common.CommandTrees.Internal; namespace System.Data.Common.CommandTrees { /// <summary> /// Describes the different "kinds" (classes) of expressions /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] public enum DbExpressionKind { /// <summary> /// True for all. /// </summary> All = 0, /// <summary> /// Logical And. /// </summary> And = 1, /// <summary> /// True for any. /// </summary> Any = 2, /// <summary> /// Conditional case statement. /// </summary> Case = 3, /// <summary> /// Polymorphic type cast. /// </summary> Cast = 4, /// <summary> /// A constant value. /// </summary> Constant = 5, /// <summary> /// Cross apply /// </summary> CrossApply = 6, /// <summary> /// Cross join /// </summary> CrossJoin = 7, /// <summary> /// Dereference. /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Deref")] Deref = 8, /// <summary> /// Duplicate removal. /// </summary> Distinct = 9, /// <summary> /// Division. /// </summary> Divide = 10, /// <summary> /// Set to singleton conversion. /// </summary> Element = 11, /// <summary> /// Entity ref value retrieval. /// </summary> EntityRef = 12, /// <summary> /// Equality /// </summary> Equals = 13, /// <summary> /// Set subtraction /// </summary> Except = 14, /// <summary> /// Restriction. /// </summary> Filter = 15, /// <summary> /// Full outer join /// </summary> FullOuterJoin = 16, /// <summary> /// Invocation of a stand-alone function /// </summary> Function = 17, /// <summary> /// Greater than. /// </summary> GreaterThan = 18, /// <summary> /// Greater than or equal. /// </summary> GreaterThanOrEquals = 19, /// <summary> /// Grouping. /// </summary> GroupBy = 20, /// <summary> /// Inner join /// </summary> InnerJoin = 21, /// <summary> /// Set intersection. /// </summary> Intersect = 22, /// <summary> /// Empty set determination. /// </summary> IsEmpty = 23, /// <summary> /// Null determination. /// </summary> IsNull = 24, /// <summary> /// Type comparison (specified Type or Subtype). /// </summary> IsOf = 25, /// <summary> /// Type comparison (specified Type only). /// </summary> IsOfOnly = 26, /// <summary> /// Application of a lambda function /// </summary> Lambda = 57, /// <summary> /// Left outer join /// </summary> LeftOuterJoin = 27, /// <summary> /// Less than. /// </summary> LessThan = 28, /// <summary> /// Less than or equal. /// </summary> LessThanOrEquals = 29, /// <summary> /// String comparison. /// </summary> Like = 30, /// <summary> /// Result count restriction (TOP n). /// </summary> Limit = 31, #if METHOD_EXPRESSION /// <summary> /// Invocation of a static or instance method. /// </summary> Method, #endif /// <summary> /// Subtraction. /// </summary> Minus = 32, /// <summary> /// Modulo. /// </summary> Modulo = 33, /// <summary> /// Multiplication. /// </summary> Multiply = 34, /// <summary> /// Instance, row, and set construction. /// </summary> NewInstance = 35, /// <summary> /// Logical Not. /// </summary> Not = 36, /// <summary> /// Inequality. /// </summary> NotEquals = 37, /// <summary> /// Null. /// </summary> Null = 38, /// <summary> /// Set members by type (or subtype). /// </summary> OfType = 39, /// <summary> /// Set members by (exact) type. /// </summary> OfTypeOnly = 40, /// <summary> /// Logical Or. /// </summary> Or = 41, /// <summary> /// Outer apply. /// </summary> OuterApply = 42, /// <summary> /// A reference to a parameter. /// </summary> ParameterReference = 43, /// <summary> /// Addition. /// </summary> Plus = 44, /// <summary> /// Projection. /// </summary> Project = 45, /// <summary> /// Retrieval of a static or instance property. /// </summary> Property = 46, /// <summary> /// Reference. /// </summary> Ref = 47, /// <summary> /// Ref key value retrieval. /// </summary> RefKey = 48, /// <summary> /// Navigation of a (composition or association) relationship. /// </summary> RelationshipNavigation = 49, /// <summary> /// Entity or relationship set scan. /// </summary> Scan = 50, /// <summary> /// Skip elements of an ordered collection. /// </summary> Skip = 51, /// <summary> /// Sorting. /// </summary> Sort = 52, /// <summary> /// Type conversion. /// </summary> Treat = 53, /// <summary> /// Negation. /// </summary> UnaryMinus = 54, /// <summary> /// Set union (with duplicates). /// </summary> UnionAll = 55, /// <summary> /// A reference to a variable. /// </summary> VariableReference = 56 } /// <summary> /// The base type for all expressions /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] public abstract class DbExpression { private readonly TypeUsage _type; private readonly DbExpressionKind _kind; internal DbExpression(DbExpressionKind kind, TypeUsage type) { CheckExpressionKind(kind); _kind = kind; Debug.Assert(type != null, string.Format(CultureInfo.InvariantCulture, "{0}.Type is null in DbExpression constructor", this.GetType().Name)); if (!TypeSemantics.IsNullable(type)) { type = type.ShallowCopy(new FacetValues { Nullable = true }); } Debug.Assert(type.IsReadOnly, "Editable type metadata specified for DbExpression.Type"); this._type = type; } /// <summary> /// Gets the type metadata for the result type of the expression. /// </summary> public TypeUsage ResultType { get { return _type; } } /// <summary> /// Gets the kind of the expression, which indicates the operation of this expression. /// </summary> public DbExpressionKind ExpressionKind { get { return _kind; } } /// <summary> /// The visitor pattern interface method for expression visitors that do not produce a result value. /// </summary> /// <param name="visitor">An instance of DbExpressionVisitor.</param> /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> public abstract void Accept(DbExpressionVisitor visitor); /// <summary> /// The visitor pattern interface method for expression visitors that produce a result value of a specific type. /// </summary> /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> public abstract TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor); #region Equals / GetHashCode // Dev10#547254: Easy to confuse DbExpressionBuilder.Equal with object.Equals method // The object.Equals method is overriden on DbExpression and marked so that it does // not appear in IntelliSense to avoid confusion with the DbExpressionBuilder.Equal // expression construction method. Overriding Equals also requires that GetHashCode // is overridden, however in both cases we defer to the System.Object implementation. [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } #endregion #region Implicit Cast Operators /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified binary value, which may be <c>null</c> /// </summary> /// <param name="value">The binary value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified binary value</returns> public static DbExpression FromBinary(byte[] value) { if (null == value) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Binary); } return ExpressionBuilder.DbExpressionBuilder.Constant(value); } public static implicit operator DbExpression(byte[] value) { return DbExpression.FromBinary(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) Boolean value /// </summary> /// <param name="value">The Boolean value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified Boolean value</returns> public static DbExpression FromBoolean(bool? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Boolean); } return (value.Value ? ExpressionBuilder.DbExpressionBuilder.True : ExpressionBuilder.DbExpressionBuilder.False); } public static implicit operator DbExpression(bool? value) { return DbExpression.FromBoolean(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) byte value /// </summary> /// <param name="value">The byte value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified byte value</returns> public static DbExpression FromByte(byte? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Byte); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(byte? value) { return DbExpression.FromByte(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) <see cref="DateTime"/> value /// </summary> /// <param name="value">The DateTime value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified DateTime value</returns> public static DbExpression FromDateTime(DateTime? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.DateTime); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(DateTime? value) { return DbExpression.FromDateTime(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) <see cref="DateTimeOffset"/> value /// </summary> /// <param name="value">The DateTimeOffset value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified DateTimeOffset value</returns> public static DbExpression FromDateTimeOffset(DateTimeOffset? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.DateTimeOffset); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(DateTimeOffset? value) { return DbExpression.FromDateTimeOffset(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) decimal value /// </summary> /// <param name="value">The decimal value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified decimal value</returns> public static DbExpression FromDecimal(decimal? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Decimal); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(decimal? value) { return DbExpression.FromDecimal(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) double value /// </summary> /// <param name="value">The double value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified double value</returns> public static DbExpression FromDouble(double? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Double); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(double? value) { return DbExpression.FromDouble(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified <see cref="DbGeography"/> value, which may be null. /// </summary> /// <param name="value">The DbGeography value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified DbGeography value</returns> public static DbExpression FromGeography(DbGeography value) { if (value == null) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Geography); } return ExpressionBuilder.DbExpressionBuilder.Constant(value); } public static implicit operator DbExpression(DbGeography value) { return DbExpression.FromGeography(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified <see cref="DbGeometry"/> value, which may be null. /// </summary> /// <param name="value">The DbGeometry value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified DbGeometry value</returns> public static DbExpression FromGeometry(DbGeometry value) { if (value == null) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Geometry); } return ExpressionBuilder.DbExpressionBuilder.Constant(value); } public static implicit operator DbExpression(DbGeometry value) { return DbExpression.FromGeometry(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) <see cref="Guid"/> value /// </summary> /// <param name="value">The Guid value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified Guid value</returns> public static DbExpression FromGuid(Guid? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Guid); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(Guid? value) { return DbExpression.FromGuid(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) Int16 value /// </summary> /// <param name="value">The Int16 value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified Int16 value</returns> public static DbExpression FromInt16(short? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Int16); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(short? value) { return DbExpression.FromInt16(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) Int32 value /// </summary> /// <param name="value">The Int32 value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified Int32 value</returns> public static DbExpression FromInt32(int? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Int32); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(int? value) { return DbExpression.FromInt32(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) Int64 value /// </summary> /// <param name="value">The Int64 value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified Int64 value</returns> public static DbExpression FromInt64(long? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Int64); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(long? value) { return DbExpression.FromInt64(value); } //// //public static implicit operator DbExpression(sbyte? value) //{ // return DbExpression.FromSByte(value); //} /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified (nullable) Single value /// </summary> /// <param name="value">The Single value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified Single value</returns> public static DbExpression FromSingle(float? value) { if (!value.HasValue) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.Single); } return ExpressionBuilder.DbExpressionBuilder.Constant(value.Value); } public static implicit operator DbExpression(float? value) { return DbExpression.FromSingle(value); } /// <summary> /// Creates a <see cref="DbExpression"/> that represents the specified string value /// </summary> /// <param name="value">The string value on which the returned expression should be based</param> /// <returns>A <see cref="DbExpression"/> that represents the specified string value</returns> public static DbExpression FromString(string value) { if (null == value) { return ExpressionBuilder.DbExpressionBuilder.CreatePrimitiveNullExpression(PrimitiveTypeKind.String); } return ExpressionBuilder.DbExpressionBuilder.Constant(value); } public static implicit operator DbExpression(string value) { return DbExpression.FromString(value); } // #endregion #region Internal API /// <summary> /// Produces a text-based tree representation of the DbExpression tree rooted at this expression /// </summary> /// <returns>A string containing the text-based tree representation</returns> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal string Print() { return new ExpressionPrinter().Print(this); } internal static void CheckExpressionKind(DbExpressionKind kind) { // Add new valid DbExpressionKind values to this method as well as the enum itself. // DbExpressionKind is a contiguous enum from All = 0 through View if ((kind < DbExpressionKind.All) || (DbExpressionKind.Lambda < kind)) { throw EntityUtil.InvalidEnumerationValue(typeof(DbExpressionKind), (int)kind); } } #endregion } /// <summary> /// The abstract base type for expressions that accept two expression operands. /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] public abstract class DbBinaryExpression : DbExpression { private readonly DbExpression _left; private readonly DbExpression _right; internal DbBinaryExpression(DbExpressionKind kind, TypeUsage type, DbExpression left, DbExpression right) : base(kind, type) { Debug.Assert(left != null, "DbBinaryExpression.Left cannot be null"); Debug.Assert(right != null, "DbBinaryExpression.Right cannot be null"); this._left = left; this._right = right; } /// <summary> /// Gets the <see cref="DbExpression"/> that defines the left argument. /// </summary> public DbExpression Left { get { return _left; } } /// <summary> /// Gets the <see cref="DbExpression"/> that defines the right argument. /// </summary> public DbExpression Right { get { return _right; } } } /// <summary> /// The abstract base type for expressions that accept a single expression operand /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] public abstract class DbUnaryExpression : DbExpression { private readonly DbExpression _argument; internal DbUnaryExpression(DbExpressionKind kind, TypeUsage resultType, DbExpression argument) : base(kind, resultType) { Debug.Assert(argument != null, "DbUnaryExpression.Argument cannot be null"); this._argument = argument; } /// <summary> /// Gets the <see cref="DbExpression"/> that defines the argument. /// </summary> public DbExpression Argument { get { return this._argument; } } } }