internal override SqlNode VisitMember(SqlMember m)
			{
				m.Expression = this.VisitExpression(m.Expression);
				if(IsNullableValue(m))
				{
					return sql.UnaryValueOf(m.Expression, m.SourceExpression);
				}
				if(IsNullableHasValue(m))
				{
					return sql.Unary(SqlNodeType.IsNotNull, m.Expression, m.SourceExpression);
				}
				return m;
			}
示例#2
0
 internal virtual SqlNode VisitMember(SqlMember m) {
     m.Expression = this.VisitExpression(m.Expression);
     return m;
 }
		private Type GenerateMember(SqlMember m)
		{
			FieldInfo fi = m.Member as FieldInfo;
			if(fi != null)
			{
				this.GenerateExpressionForType(m.Expression, m.Expression.ClrType);
				gen.Emit(OpCodes.Ldfld, fi);
				return fi.FieldType;
			}
			else
			{
				PropertyInfo pi = (PropertyInfo)m.Member;
				return this.GenerateMethodCall(new SqlMethodCall(m.ClrType, m.SqlType, pi.GetGetMethod(), m.Expression, null, m.SourceExpression));
			}
		}
		internal override SqlNode VisitMember(SqlMember m)
		{
			this.Visit(m.Expression);
			_commandStringBuilder.Append(".");
			_commandStringBuilder.Append(m.Member.Name);
			return m;
		}
		internal override SqlNode VisitMember(SqlMember m)
		{
			return new SqlMember(m.ClrType, m.SqlType, (SqlExpression)this.Visit(m.Expression), m.Member);
		}
		private SqlNode AccessMember(SqlMember m, SqlExpression expo) {
			SqlExpression exp = expo;

			switch (exp.NodeType) {
				case SqlNodeType.ClientCase: {
					// Distribute into each case.
					SqlClientCase sc = (SqlClientCase)exp;
					Type newClrType = null;
					List<SqlExpression> matches = new List<SqlExpression>();
					List<SqlExpression> values = new List<SqlExpression>();
					foreach (SqlClientWhen when in sc.Whens) {
						SqlExpression newValue = (SqlExpression)AccessMember(m, when.Value);
						if (newClrType == null) {
							newClrType = newValue.ClrType;
						}
						else if (newClrType != newValue.ClrType) {
							throw Error.ExpectedClrTypesToAgree(newClrType, newValue.ClrType);
						}
						matches.Add(when.Match);
						values.Add(newValue);
					}

					SqlExpression result = sql.Case(newClrType, sc.Expression, matches, values, sc.SourceExpression);
					return result;
				}
				case SqlNodeType.SimpleCase: {
					// Distribute into each case.
					SqlSimpleCase sc = (SqlSimpleCase)exp;
					Type newClrType = null;
					List<SqlExpression> newMatches = new List<SqlExpression>();
					List<SqlExpression> newValues = new List<SqlExpression>();
					foreach (SqlWhen when in sc.Whens) {
						SqlExpression newValue = (SqlExpression)AccessMember(m, when.Value);
						if (newClrType == null) {
							newClrType = newValue.ClrType;
						}
						else if (newClrType != newValue.ClrType) {
							throw Error.ExpectedClrTypesToAgree(newClrType, newValue.ClrType);
						}
						newMatches.Add(when.Match);
						newValues.Add(newValue);
					}
					SqlExpression result = sql.Case(newClrType, sc.Expression, newMatches, newValues, sc.SourceExpression);
					return result;
				}
				case SqlNodeType.SearchedCase: {
					// Distribute into each case.
					SqlSearchedCase sc = (SqlSearchedCase)exp;
					List<SqlWhen> whens = new List<SqlWhen>(sc.Whens.Count);
					foreach (SqlWhen when in sc.Whens) {
						SqlExpression value = (SqlExpression)AccessMember(m, when.Value);
						whens.Add(new SqlWhen(when.Match, value));
					}
					SqlExpression @else = (SqlExpression)AccessMember(m, sc.Else);
					return sql.SearchedCase(whens.ToArray(), @else, sc.SourceExpression);
				}
				case SqlNodeType.TypeCase: {
					// We don't allow derived types to map members to different database fields.
					// Therefore, just pick the best SqlNew to call AccessMember on.
					SqlTypeCase tc = (SqlTypeCase)exp;

					// Find the best type binding for this member.
					SqlNew tb = tc.Whens[0].TypeBinding as SqlNew;
					foreach (SqlTypeCaseWhen when in tc.Whens) {
						if (when.TypeBinding.NodeType == SqlNodeType.New) {
							SqlNew sn = (SqlNew)when.TypeBinding;
							if (m.Member.DeclaringType.IsAssignableFrom(sn.ClrType)) {
								tb = sn;
								break;
							}
						}
					}
					return AccessMember(m, tb);
				}
				case SqlNodeType.AliasRef: {
					// convert alias.Member => column
					SqlAliasRef aref = (SqlAliasRef)exp;
					// if its a table, find the matching column
					SqlTable tab = aref.Alias.Node as SqlTable;
					if (tab != null) {
						MetaDataMember mm = GetRequiredInheritanceDataMember(tab.RowType, m.Member);
						Diagnostics.Debug.Assert(mm != null);
						string name = mm.MappedName;
						SqlColumn c = tab.Find(name);
						if (c == null) {
							ProviderType sqlType = sql.Default(mm);
							c = new SqlColumn(m.ClrType, sqlType, name, mm, null, m.SourceExpression);
							c.Alias = aref.Alias;
							tab.Columns.Add(c);
						}
						return new SqlColumnRef(c);
					}
					// if it is a table valued function, find the matching result column                                
					SqlTableValuedFunctionCall fc = aref.Alias.Node as SqlTableValuedFunctionCall;
					if (fc != null) {
						MetaDataMember mm = GetRequiredInheritanceDataMember(fc.RowType, m.Member);
						Diagnostics.Debug.Assert(mm != null);
						string name = mm.MappedName;
						SqlColumn c = fc.Find(name);
						if (c == null) {
							ProviderType sqlType = sql.Default(mm);
							c = new SqlColumn(m.ClrType, sqlType, name, mm, null, m.SourceExpression);
							c.Alias = aref.Alias;
							fc.Columns.Add(c);
						}
						return new SqlColumnRef(c);
					}
					break;
				}
				case SqlNodeType.OptionalValue:
					// convert option(exp).Member => exp.Member
					return this.AccessMember(m, ((SqlOptionalValue)exp).Value);

				case SqlNodeType.OuterJoinedValue: {
					SqlNode n = this.AccessMember(m, ((SqlUnary)exp).Operand);
					SqlExpression e = n as SqlExpression;
					if (e != null) return sql.Unary(SqlNodeType.OuterJoinedValue, e);
					return n;
				}

				case SqlNodeType.Lift:
					return this.AccessMember(m, ((SqlLift)exp).Expression);

				case SqlNodeType.UserRow: {
					// convert UserRow.Member => UserColumn
					SqlUserRow row = (SqlUserRow)exp;
					SqlUserQuery suq = row.Query;
					MetaDataMember mm = GetRequiredInheritanceDataMember(row.RowType, m.Member);
					Diagnostics.Debug.Assert(mm != null);
					string name = mm.MappedName;
					SqlUserColumn c = suq.Find(name);
					if (c == null) {
						ProviderType sqlType = sql.Default(mm);
						c = new SqlUserColumn(m.ClrType, sqlType, suq, name, mm.IsPrimaryKey, m.SourceExpression);
						suq.Columns.Add(c);
					}
					return c;
				}
				case SqlNodeType.New: {
					// convert (new {Member = expr}).Member => expr
					SqlNew sn = (SqlNew)exp;
					SqlExpression e = sn.Find(m.Member);
					if (e != null) {
						return e;
					}
					MetaDataMember mm = sn.MetaType.PersistentDataMembers.FirstOrDefault(p => p.Member == m.Member);
					if (!sn.SqlType.CanBeColumn && mm != null) {
						throw Error.MemberNotPartOfProjection(m.Member.DeclaringType, m.Member.Name);
					}
					break;
				}
				case SqlNodeType.Element:
				case SqlNodeType.ScalarSubSelect: {
					// convert Scalar/Element(select exp).Member => Scalar/Element(select exp.Member) / select exp.Member
					SqlSubSelect sub = (SqlSubSelect)exp;
					SqlAlias alias = new SqlAlias(sub.Select);
					SqlAliasRef aref = new SqlAliasRef(alias);

					SqlSelect saveSelect = this.currentSelect;
					try {
						SqlSelect newSelect = new SqlSelect(aref, alias, sub.SourceExpression);
						this.currentSelect = newSelect;
						SqlNode result = this.Visit(sql.Member(aref, m.Member));

						SqlExpression rexp = result as SqlExpression;
						if (rexp != null) {

							// If the expression is still a Member after being visited, but it cannot be a column, then it cannot be collapsed
							// into the SubSelect because we need to keep track of the fact that this member has to be accessed on the client.
							// This must be done after the expression has been Visited above, because otherwise we don't have
							// enough context to know if the member can be a column or not.
							if (rexp.NodeType == SqlNodeType.Member && !SqlColumnizer.CanBeColumn(rexp)) {
								// If the original member expression is an Element, optimize it by converting to an OuterApply if possible.
								// We have to do this here because we are creating a new member expression based on it, and there are no
								// subsequent visitors that will do this optimization.
								if (this.canUseOuterApply && exp.NodeType == SqlNodeType.Element && this.currentSelect != null) {
									// Reset the currentSelect since we are not going to use the previous SqlSelect that was created
									this.currentSelect = saveSelect;                                            
									this.currentSelect.From = sql.MakeJoin(SqlJoinType.OuterApply, this.currentSelect.From, alias, null, sub.SourceExpression);
									exp = this.VisitExpression(aref);
								}                                        
								return sql.Member(exp, m.Member);
							}

							// Since we are going to make a SubSelect out of this member expression, we need to make
							// sure it gets columnized before it gets to the PostBindDotNetConverter, otherwise only the
							// entire SubSelect will be columnized as a whole. Subsequent columnization does not know how to handle
							// any function calls that may be produced by the PostBindDotNetConverter, but we know how to handle it here.
							newSelect.Selection = rexp;
							newSelect.Selection = this.columnizer.ColumnizeSelection(newSelect.Selection);
							newSelect.Selection = this.ConvertLinks(newSelect.Selection);
							SqlNodeType subType = (rexp is SqlTypeCase || !rexp.SqlType.CanBeColumn) ? SqlNodeType.Element : SqlNodeType.ScalarSubSelect;
							SqlSubSelect subSel = sql.SubSelect(subType, newSelect);
							return this.FoldSubquery(subSel);
						}

						SqlSelect rselect = result as SqlSelect;
						if (rselect != null) {
							SqlAlias ralias = new SqlAlias(rselect);
							SqlAliasRef rref = new SqlAliasRef(ralias);
							newSelect.Selection = this.ConvertLinks(this.VisitExpression(rref));
							newSelect.From = new SqlJoin(SqlJoinType.CrossApply, alias, ralias, null, m.SourceExpression);
							return newSelect;
						}
						throw Error.UnexpectedNode(result.NodeType);
					}
					finally {
						this.currentSelect = saveSelect;
					}
				}
				case SqlNodeType.Value: {
					SqlValue val = (SqlValue)exp;
					if (val.Value == null) {
						return sql.Value(m.ClrType, m.SqlType, null, val.IsClientSpecified, m.SourceExpression);
					}
					else if (m.Member is PropertyInfo) {
						PropertyInfo p = (PropertyInfo)m.Member;
						return sql.Value(m.ClrType, m.SqlType, p.GetValue(val.Value, null), val.IsClientSpecified, m.SourceExpression);
					}
					else {
						FieldInfo f = (FieldInfo)m.Member;
						return sql.Value(m.ClrType, m.SqlType, f.GetValue(val.Value), val.IsClientSpecified, m.SourceExpression);
					}
				}
				case SqlNodeType.Grouping: {
					SqlGrouping g = ((SqlGrouping)exp);
					if (m.Member.Name == "Key") {
						return g.Key;
					}
					break;
				}
				case SqlNodeType.ClientParameter: {
					SqlClientParameter cp = (SqlClientParameter)exp;
					// create new accessor including this member access
					LambdaExpression accessor =
						Expression.Lambda(
										  typeof(Func<,>).MakeGenericType(typeof(object[]), m.ClrType),
							Expression.MakeMemberAccess(cp.Accessor.Body, m.Member),
							cp.Accessor.Parameters
							);
					return new SqlClientParameter(m.ClrType, m.SqlType, accessor, cp.SourceExpression);
				}
				default:
					break;  
			}
			if (m.Expression == exp) {
				return m;
			}
			else {
				return sql.Member(exp, m.Member);
			}
		}
		internal override SqlNode VisitMember(SqlMember m) {
			return this.AccessMember(m, this.FetchExpression(m.Expression));
		}
		private static bool IsNullableHasValue(SqlMember m)
		{
			return TypeSystem.IsNullableType(m.Expression.ClrType) && m.Member.Name == "HasValue";
		}
		private static bool IsSupportedTimeSpanMember(SqlMember m)
		{
			if(m.Expression.ClrType == typeof(TimeSpan))
			{
				switch(m.Member.Name)
				{
					case "Ticks":
					case "TotalMilliseconds":
					case "TotalSeconds":
					case "TotalMinutes":
					case "TotalHours":
					case "TotalDays":
					case "Milliseconds":
					case "Seconds":
					case "Minutes":
					case "Hours":
					case "Days":
						return true;
				}
			}
			return false;
		}
		private static bool IsSupportedMember(SqlMember m)
		{
			return IsNullableHasValue(m) || IsNullableHasValue(m);
		}
		//
		// Identical to IsSupportedDateTimeMember(), except for support for 'DateTime'
		//
		private static bool IsSupportedDateTimeOffsetMember(SqlMember m)
		{
			if(m.Expression.ClrType == typeof(DateTimeOffset))
			{
				string datePart = GetDatePart(m.Member.Name);
				if(datePart != null)
				{
					return true;
				}
				switch(m.Member.Name)
				{
					case "Date":
					case "DateTime":
					case "TimeOfDay":
					case "DayOfWeek":
						return true;
				}
			}
			return false;
		}
		private static bool IsSupportedBinaryMember(SqlMember m)
		{
			return m.Expression.ClrType == typeof(Binary) && m.Member.Name == "Length";
		}
		private static bool IsSupportedStringMember(SqlMember m)
		{
			return m.Expression.ClrType == typeof(string) && m.Member.Name == "Length";
		}
		private static bool IsSupportedMember(SqlMember m)
		{
			return IsSupportedStringMember(m)
				|| IsSupportedBinaryMember(m)
				|| IsSupportedDateTimeMember(m)
				|| IsSupportedDateTimeOffsetMember(m)
				|| IsSupportedTimeSpanMember(m);
		}
			internal override SqlNode VisitMember(SqlMember m)
			{
				SqlExpression exp = this.VisitExpression(m.Expression);
				MemberInfo member = m.Member;
				Expression source = m.SourceExpression;

				Type baseClrTypeOfExpr = TypeSystem.GetNonNullableType(exp.ClrType);
				if(baseClrTypeOfExpr == typeof(string) && member.Name == "Length")
				{
					// This gives a different result than .Net would if the string ends in spaces.
					// We decided not to fix this up (e.g. LEN(@s+'#') - 1) since it would incur a performance hit and 
					// people may actually expect that it translates to the SQL LEN function.
					return sql.FunctionCallStringLength(exp);
				}
				else if(baseClrTypeOfExpr == typeof(Binary) && member.Name == "Length")
				{
					return sql.FunctionCallDataLength(exp);
				}
				else if(baseClrTypeOfExpr == typeof(DateTime) || baseClrTypeOfExpr == typeof(DateTimeOffset))
				{
					string datePart = GetDatePart(member.Name);
					if(datePart != null)
					{
						return sql.FunctionCallDatePart(datePart, exp);
					}
					else if(member.Name == "Date")
					{
						if(this.providerMode == SqlServerProviderMode.Sql2008)
						{
							SqlExpression date = new SqlVariable(typeof(void), null, "DATE", source);
							return sql.FunctionCall(typeof(DateTime), "CONVERT", new SqlExpression[2] { date, exp }, source);
						}
						// date --> dateadd(hh, -(datepart(hh, @date)), 
						//          dateadd(mi, -(datepart(mi, @date)), 
						//          dateadd(ss, -(datepart(ss, @date)), 
						//          dateadd(ms, -(datepart(ms, @date)), 
						//          @date))))

						SqlExpression ms = sql.FunctionCallDatePart("MILLISECOND", exp);
						SqlExpression ss = sql.FunctionCallDatePart("SECOND", exp);
						SqlExpression mi = sql.FunctionCallDatePart("MINUTE", exp);
						SqlExpression hh = sql.FunctionCallDatePart("HOUR", exp);

						SqlExpression result = exp;

						result = sql.FunctionCallDateAdd("MILLISECOND", sql.Unary(SqlNodeType.Negate, ms), result);
						result = sql.FunctionCallDateAdd("SECOND", sql.Unary(SqlNodeType.Negate, ss), result);
						result = sql.FunctionCallDateAdd("MINUTE", sql.Unary(SqlNodeType.Negate, mi), result);
						result = sql.FunctionCallDateAdd("HOUR", sql.Unary(SqlNodeType.Negate, hh), result);

						return result;
					}
					else if(member.Name == "DateTime")
					{
						Debug.Assert(baseClrTypeOfExpr == typeof(DateTimeOffset), "'DateTime' property supported only for instances of DateTimeOffset.");
						SqlExpression datetime = new SqlVariable(typeof(void), null, "DATETIME", source);
						return sql.FunctionCall(typeof(DateTime), "CONVERT", new SqlExpression[2] { datetime, exp }, source);
					}
					else if(member.Name == "TimeOfDay")
					{
						SqlExpression hours = sql.FunctionCallDatePart("HOUR", exp);
						SqlExpression minutes = sql.FunctionCallDatePart("MINUTE", exp);
						SqlExpression seconds = sql.FunctionCallDatePart("SECOND", exp);
						SqlExpression milliseconds = sql.FunctionCallDatePart("MILLISECOND", exp);

						SqlExpression ticksFromHour = sql.Multiply(sql.ConvertToBigint(hours), TimeSpan.TicksPerHour);
						SqlExpression ticksFromMinutes = sql.Multiply(sql.ConvertToBigint(minutes), TimeSpan.TicksPerMinute);
						SqlExpression ticksFromSeconds = sql.Multiply(sql.ConvertToBigint(seconds), TimeSpan.TicksPerSecond);
						SqlExpression ticksFromMs = sql.Multiply(sql.ConvertToBigint(milliseconds), TimeSpan.TicksPerMillisecond);
						return sql.ConvertTo(typeof(TimeSpan), sql.Add(ticksFromHour, ticksFromMinutes, ticksFromSeconds, ticksFromMs));
					}
					else if(member.Name == "DayOfWeek")
					{
						//(DATEPART(dw,@date) + @@Datefirst + 6) % 7 to make it independent from SQL settings
						SqlExpression sqlDay = sql.FunctionCallDatePart("dw", exp);

						// 
						// .DayOfWeek returns a System.DayOfWeek, so ConvertTo that enum.
						return sql.ConvertTo(typeof(DayOfWeek),
								sql.Mod(
								  sql.Add(sqlDay,
									 sql.Add(new SqlVariable(typeof(int), sql.Default(typeof(int)), "@@DATEFIRST", source), 6)
								  )
								, 7));
					}
				}
				else if(baseClrTypeOfExpr == typeof(System.TimeSpan))
				{
					switch(member.Name)
					{
						case "Ticks":
							if(sql.IsTimeType(exp))
							{
								return this.sql.Divide(
											sql.ConvertToBigint(
												sql.Add(
													this.sql.Multiply(sql.ConvertToBigint(sql.FunctionCallDatePart("HOUR", exp)), 3600000000000),
													this.sql.Multiply(sql.ConvertToBigint(sql.FunctionCallDatePart("MINUTE", exp)), 60000000000),
													this.sql.Multiply(sql.ConvertToBigint(sql.FunctionCallDatePart("SECOND", exp)), 1000000000),
													sql.FunctionCallDatePart("NANOSECOND", exp))
												),
											100
									);
							}
							return sql.ConvertToBigint(exp);
						case "TotalMilliseconds":
							if(sql.IsTimeType(exp))
							{
								return this.sql.Add(
											this.sql.Multiply(sql.FunctionCallDatePart("HOUR", exp), 3600000),
											this.sql.Multiply(sql.FunctionCallDatePart("MINUTE", exp), 60000),
											this.sql.Multiply(sql.FunctionCallDatePart("SECOND", exp), 1000),
											this.sql.Divide(sql.ConvertToDouble(sql.ConvertToBigint(sql.FunctionCallDatePart("NANOSECOND", exp))), 1000000)
										);
							}
							return sql.Divide(sql.ConvertToDouble(exp), TimeSpan.TicksPerMillisecond);
						case "TotalSeconds":
							if(sql.IsTimeType(exp))
							{
								return this.sql.Add(
											this.sql.Multiply(sql.FunctionCallDatePart("HOUR", exp), 3600),
											this.sql.Multiply(sql.FunctionCallDatePart("MINUTE", exp), 60),
											this.sql.FunctionCallDatePart("SECOND", exp),
											this.sql.Divide(sql.ConvertToDouble(sql.ConvertToBigint(sql.FunctionCallDatePart("NANOSECOND", exp))), 1000000000)
										);
							}
							return sql.Divide(sql.ConvertToDouble(exp), TimeSpan.TicksPerSecond);
						case "TotalMinutes":
							if(sql.IsTimeType(exp))
							{
								return this.sql.Add(
											this.sql.Multiply(sql.FunctionCallDatePart("HOUR", exp), 60),
											this.sql.FunctionCallDatePart("MINUTE", exp),
											this.sql.Divide(sql.ConvertToDouble(sql.FunctionCallDatePart("SECOND", exp)), 60),
											this.sql.Divide(sql.ConvertToDouble(sql.ConvertToBigint(sql.FunctionCallDatePart("NANOSECOND", exp))), 60000000000)
										);
							}
							return sql.Divide(sql.ConvertToDouble(exp), TimeSpan.TicksPerMinute);
						case "TotalHours":
							if(sql.IsTimeType(exp))
							{
								return this.sql.Add(
											this.sql.FunctionCallDatePart("HOUR", exp),
											this.sql.Divide(sql.ConvertToDouble(sql.FunctionCallDatePart("MINUTE", exp)), 60),
											this.sql.Divide(sql.ConvertToDouble(sql.FunctionCallDatePart("SECOND", exp)), 3600),
											this.sql.Divide(sql.ConvertToDouble(sql.ConvertToBigint(sql.FunctionCallDatePart("NANOSECOND", exp))), 3600000000000)
										);
							}
							return sql.Divide(sql.ConvertToDouble(exp), TimeSpan.TicksPerHour);
						case "TotalDays":
							if(sql.IsTimeType(exp))
							{
								return this.sql.Divide(
											this.sql.Add(
												this.sql.FunctionCallDatePart("HOUR", exp),
												this.sql.Divide(sql.ConvertToDouble(sql.FunctionCallDatePart("MINUTE", exp)), 60),
												this.sql.Divide(sql.ConvertToDouble(sql.FunctionCallDatePart("SECOND", exp)), 3600),
												this.sql.Divide(sql.ConvertToDouble(sql.ConvertToBigint(sql.FunctionCallDatePart("NANOSECOND", exp))), 3600000000000)),
											24
										);
							}
							return sql.Divide(sql.ConvertToDouble(exp), TimeSpan.TicksPerDay);
						case "Milliseconds":
							if(sql.IsTimeType(exp))
							{
								return this.sql.FunctionCallDatePart("MILLISECOND", exp);
							}
							return sql.ConvertToInt(sql.Mod(sql.ConvertToBigint(sql.Divide(exp, TimeSpan.TicksPerMillisecond)), 1000));
						case "Seconds":
							if(sql.IsTimeType(exp))
							{
								return this.sql.FunctionCallDatePart("SECOND", exp);
							}
							return sql.ConvertToInt(sql.Mod(sql.ConvertToBigint(sql.Divide(exp, TimeSpan.TicksPerSecond)), 60));
						case "Minutes":
							if(sql.IsTimeType(exp))
							{
								return this.sql.FunctionCallDatePart("MINUTE", exp);
							}
							return sql.ConvertToInt(sql.Mod(sql.ConvertToBigint(sql.Divide(exp, TimeSpan.TicksPerMinute)), 60));
						case "Hours":
							if(sql.IsTimeType(exp))
							{
								return this.sql.FunctionCallDatePart("HOUR", exp);
							}
							return sql.ConvertToInt(sql.Mod(sql.ConvertToBigint(sql.Divide(exp, TimeSpan.TicksPerHour)), 24));
						case "Days":
							if(sql.IsTimeType(exp))
							{
								return this.sql.ValueFromObject(0, false, exp.SourceExpression);
							}
							return sql.ConvertToInt(sql.Divide(exp, TimeSpan.TicksPerDay));
						default:
							throw Error.MemberCannotBeTranslated(member.DeclaringType, member.Name);
					}
				}
				throw Error.MemberCannotBeTranslated(member.DeclaringType, member.Name);
			}