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; }
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); }