internal override ProviderType GetBestType(ProviderType typeA, ProviderType typeB)
		{
			// first determine the type precedence
			SqlType bestType = (SqlType)(typeA.ComparePrecedenceTo(typeB) > 0 ? typeA : typeB);

			// if one of the types is a not a server type, return
			// that type
			if(typeA.IsApplicationType || typeA.IsRuntimeOnlyType)
			{
				return typeA;
			}
			if(typeB.IsApplicationType || typeB.IsRuntimeOnlyType)
			{
				return typeB;
			}

			SqlType sqlTypeA = (SqlType)typeA;
			SqlType sqlTypeB = (SqlType)typeB;
			if(sqlTypeA.HasPrecisionAndScale && sqlTypeB.HasPrecisionAndScale && bestType.SqlDbType == SqlDbType.Decimal)
			{
				int p0 = sqlTypeA.Precision;
				int s0 = sqlTypeA.Scale;
				int p1 = sqlTypeB.Precision;
				int s1 = sqlTypeB.Scale;
				// precision and scale may be zero if this is an unsized type.
				if(p0 == 0 && s0 == 0 && p1 == 0 && s1 == 0)
				{
					return SqlTypeSystem.Create(bestType.SqlDbType);
				}
				else if(p0 == 0 && s0 == 0)
				{
					return SqlTypeSystem.Create(bestType.SqlDbType, p1, s1);
				}
				else if(p1 == 0 && s1 == 0)
				{
					return SqlTypeSystem.Create(bestType.SqlDbType, p0, s0);
				}
				// determine best scale/precision
				int bestLeft = Math.Max(p0 - s0, p1 - s1);
				int bestRight = Math.Max(s0, s1);
				int precision = Math.Min(bestLeft + bestRight, 38);
				return SqlTypeSystem.Create(bestType.SqlDbType, precision, /*scale*/bestRight);
			}
			else
			{
				// determine the best size
				int? bestSize = null;

				if(sqlTypeA.Size.HasValue && sqlTypeB.Size.HasValue)
				{
					bestSize = (sqlTypeB.Size > sqlTypeA.Size) ? sqlTypeB.Size : sqlTypeA.Size;
				}

				if(sqlTypeB.Size.HasValue && sqlTypeB.Size.Value == ProviderConstants.LargeTypeSizeIndicator
				   || sqlTypeA.Size.HasValue && sqlTypeA.Size.Value == ProviderConstants.LargeTypeSizeIndicator)
				{
					// the large type size trumps all
					bestSize = ProviderConstants.LargeTypeSizeIndicator;
				}

				bestType = new SqlType(bestType.SqlDbType, bestSize);
			}

			return bestType;
		}
        internal override ProviderType GetBestType(ProviderType typeA, ProviderType typeB)
        {
            // first determine the type precedence
            SqlType bestType = (SqlType)(typeA.ComparePrecedenceTo(typeB) > 0 ? typeA : typeB);

            // if one of the types is a not a server type, return
            // that type
            if (typeA.IsApplicationType || typeA.IsRuntimeOnlyType)
            {
                return(typeA);
            }
            if (typeB.IsApplicationType || typeB.IsRuntimeOnlyType)
            {
                return(typeB);
            }

            SqlType sqlTypeA = (SqlType)typeA;
            SqlType sqlTypeB = (SqlType)typeB;

            if (sqlTypeA.HasPrecisionAndScale && sqlTypeB.HasPrecisionAndScale && bestType.SqlDbType == SqlDbType.Decimal)
            {
                int p0 = sqlTypeA.Precision;
                int s0 = sqlTypeA.Scale;
                int p1 = sqlTypeB.Precision;
                int s1 = sqlTypeB.Scale;
                // precision and scale may be zero if this is an unsized type.
                if (p0 == 0 && s0 == 0 && p1 == 0 && s1 == 0)
                {
                    return(SqlTypeSystem.Create(bestType.SqlDbType));
                }
                else if (p0 == 0 && s0 == 0)
                {
                    return(SqlTypeSystem.Create(bestType.SqlDbType, p1, s1));
                }
                else if (p1 == 0 && s1 == 0)
                {
                    return(SqlTypeSystem.Create(bestType.SqlDbType, p0, s0));
                }
                // determine best scale/precision
                int bestLeft  = Math.Max(p0 - s0, p1 - s1);
                int bestRight = Math.Max(s0, s1);
                int precision = Math.Min(bestLeft + bestRight, 38);
                return(SqlTypeSystem.Create(bestType.SqlDbType, precision, /*scale*/ bestRight));
            }
            else
            {
                // determine the best size
                int?bestSize = null;

                if (sqlTypeA.Size.HasValue && sqlTypeB.Size.HasValue)
                {
                    bestSize = (sqlTypeB.Size > sqlTypeA.Size) ? sqlTypeB.Size : sqlTypeA.Size;
                }

                if (sqlTypeB.Size.HasValue && sqlTypeB.Size.Value == ProviderConstants.LargeTypeSizeIndicator ||
                    sqlTypeA.Size.HasValue && sqlTypeA.Size.Value == ProviderConstants.LargeTypeSizeIndicator)
                {
                    // the large type size trumps all
                    bestSize = ProviderConstants.LargeTypeSizeIndicator;
                }

                bestType = new SqlType(bestType.SqlDbType, bestSize);
            }

            return(bestType);
        }
		internal override ProviderType PredictTypeForBinary(SqlNodeType binaryOp, ProviderType leftType, ProviderType rightType)
		{
			SqlType highest;

			if(leftType.IsSameTypeFamily(this.From(typeof(string))) && rightType.IsSameTypeFamily(this.From(typeof(string))))
			{
				highest = (SqlType)this.GetBestType(leftType, rightType);
			}
			else
			{
				int coercionPrecedence = leftType.ComparePrecedenceTo(rightType);
				highest = (SqlType)(coercionPrecedence > 0 ? leftType : rightType);
			}

			switch(binaryOp)
			{
				case SqlNodeType.Add:
				case SqlNodeType.Sub:
				case SqlNodeType.Mul:
				case SqlNodeType.Div:
				case SqlNodeType.BitAnd:
				case SqlNodeType.BitOr:
				case SqlNodeType.BitXor:
				case SqlNodeType.Mod:
				case SqlNodeType.Coalesce:
					return highest;
				case SqlNodeType.Concat:
					// When concatenating two types with size, the result type after
					// concatenation must have a size equal to the sum of the two sizes.
					if(highest.HasSizeOrIsLarge)
					{
						// get the best type, specifying null for size so we get
						// the maximum allowable size
						ProviderType concatType = this.GetBestType(highest.SqlDbType, null);

						if((!leftType.IsLargeType && leftType.Size.HasValue) &&
						   (!rightType.IsLargeType && rightType.Size.HasValue))
						{
							// If both types are not large types and have size, and the
							// size is less than the default size, return the shortened type.
							int concatSize = leftType.Size.Value + rightType.Size.Value;
							if((concatSize < concatType.Size) || concatType.IsLargeType)
							{
								return GetBestType(highest.SqlDbType, concatSize);
							}
						}

						return concatType;
					}
					return highest;
				case SqlNodeType.And:
				case SqlNodeType.Or:
				case SqlNodeType.LT:
				case SqlNodeType.LE:
				case SqlNodeType.GT:
				case SqlNodeType.GE:
				case SqlNodeType.EQ:
				case SqlNodeType.NE:
				case SqlNodeType.EQ2V:
				case SqlNodeType.NE2V:
					return ProviderConstants.IntType;
				default:
					throw Error.UnexpectedNode(binaryOp);
			}
		}
        internal override ProviderType PredictTypeForBinary(SqlNodeType binaryOp, ProviderType leftType, ProviderType rightType)
        {
            SqlType highest;

            if (leftType.IsSameTypeFamily(this.From(typeof(string))) && rightType.IsSameTypeFamily(this.From(typeof(string))))
            {
                highest = (SqlType)this.GetBestType(leftType, rightType);
            }
            else
            {
                int coercionPrecedence = leftType.ComparePrecedenceTo(rightType);
                highest = (SqlType)(coercionPrecedence > 0 ? leftType : rightType);
            }

            switch (binaryOp)
            {
            case SqlNodeType.Add:
            case SqlNodeType.Sub:
            case SqlNodeType.Mul:
            case SqlNodeType.Div:
            case SqlNodeType.BitAnd:
            case SqlNodeType.BitOr:
            case SqlNodeType.BitXor:
            case SqlNodeType.Mod:
            case SqlNodeType.Coalesce:
                return(highest);

            case SqlNodeType.Concat:
                // When concatenating two types with size, the result type after
                // concatenation must have a size equal to the sum of the two sizes.
                if (highest.HasSizeOrIsLarge)
                {
                    // get the best type, specifying null for size so we get
                    // the maximum allowable size
                    ProviderType concatType = this.GetBestType(highest.SqlDbType, null);

                    if ((!leftType.IsLargeType && leftType.Size.HasValue) &&
                        (!rightType.IsLargeType && rightType.Size.HasValue))
                    {
                        // If both types are not large types and have size, and the
                        // size is less than the default size, return the shortened type.
                        int concatSize = leftType.Size.Value + rightType.Size.Value;
                        if ((concatSize < concatType.Size) || concatType.IsLargeType)
                        {
                            return(GetBestType(highest.SqlDbType, concatSize));
                        }
                    }

                    return(concatType);
                }
                return(highest);

            case SqlNodeType.And:
            case SqlNodeType.Or:
            case SqlNodeType.LT:
            case SqlNodeType.LE:
            case SqlNodeType.GT:
            case SqlNodeType.GE:
            case SqlNodeType.EQ:
            case SqlNodeType.NE:
            case SqlNodeType.EQ2V:
            case SqlNodeType.NE2V:
                return(ProviderConstants.IntType);

            default:
                throw Error.UnexpectedNode(binaryOp);
            }
        }