internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { if(this.ingoreExternalRefs && !this.nodeMap.ContainsKey(aref.Alias)) { return aref; } return new SqlAliasRef((SqlAlias)this.Visit(aref.Alias)); }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { SqlAlias alias = aref.Alias; SqlAlias value; if(_removedMap.TryGetValue(alias, out value)) { throw Error.InvalidReferenceToRemovedAliasDuringDeflation(); } return aref; }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { SqlExpression result = base.VisitAliasRef(aref); if (result != null && result == aref) { // reference to outer scope, don't propogate references to expressions or aliases SqlAlias alias = aref.Alias; SqlAlias newalias = new SqlAlias(new SqlNop(aref.ClrType, aref.SqlType, null)); return new SqlAliasRef(newalias); } return result; }
internal SqlSelect BuildDefaultQuery(MetaType rowType, bool allowDeferred, SqlLink link, Expression source) { System.Diagnostics.Debug.Assert(rowType != null && rowType.Table != null); if (rowType.HasInheritance && rowType.InheritanceRoot != rowType) { // RowType is expected to be an inheritance root. throw Error.ArgumentWrongValue("rowType"); } SqlTable table = sql.Table(rowType.Table, rowType, source); SqlAlias tableAlias = new SqlAlias(table); SqlAliasRef tableAliasRef = new SqlAliasRef(tableAlias); SqlExpression projection = this.BuildProjection(tableAliasRef, table.RowType, allowDeferred, link, source); return new SqlSelect(projection, tableAlias, source); }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { SqlNode node = aref.Alias.Node; if(node is SqlTable || node is SqlTableValuedFunctionCall) { return aref; } SqlUnion union = node as SqlUnion; if(union != null) { return this.ExpandUnion(union); } SqlSelect ss = node as SqlSelect; if(ss != null) { return this.VisitExpression(ss.Selection); } SqlExpression exp = node as SqlExpression; if(exp != null) return this.VisitExpression(exp); throw Error.CouldNotHandleAliasRef(node.NodeType); }
private SqlNode VisitAggregate(Expression sequence, LambdaExpression lambda, SqlNodeType aggType, Type returnType) { // Convert seq.Agg(exp) into // // 1) SELECT Agg(exp) FROM seq // 2) SELECT Agg1 FROM (SELECT Agg(exp) as Agg1 FROM group-seq GROUP BY ...) // 3) SCALAR(SELECT Agg(exp) FROM seq) // bool isCount = aggType == SqlNodeType.Count || aggType == SqlNodeType.LongCount; SqlNode source = this.Visit(sequence); SqlSelect select = this.CoerceToSequence(source); SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); // If the sequence is of the form x.Select(expr).Agg() and the lambda for the aggregate is null, // or is a no-op parameter expression (like u=>u), clone the group by selection lambda // expression, and use for the aggregate. // Final form should be x.Agg(expr) MethodCallExpression mce = sequence as MethodCallExpression; if(!_outerNode && !isCount && (lambda == null || (lambda.Parameters.Count == 1 && lambda.Parameters[0] == lambda.Body)) && (mce != null) && IsSequenceOperatorCall(mce, "Select") && select.From is SqlAlias) { LambdaExpression selectionLambda = GetLambda(mce.Arguments[1]); lambda = Expression.Lambda(selectionLambda.Type, selectionLambda.Body, selectionLambda.Parameters); alias = (SqlAlias)select.From; aref = new SqlAliasRef(alias); } if(lambda != null && !TypeSystem.IsSimpleType(lambda.Body.Type)) { throw Error.CannotAggregateType(lambda.Body.Type); } //Empty parameter aggregates are not allowed on anonymous types //i.e. db.Customers.Select(c=>new{c.Age}).Max() instead it should be // db.Customers.Select(c=>new{c.Age}).Max(c=>c.Age) if(select.Selection.SqlType.IsRuntimeOnlyType && !IsGrouping(sequence.Type) && !isCount && lambda == null) { throw Error.NonCountAggregateFunctionsAreNotValidOnProjections(aggType); } if(lambda != null) _parameterExpressionToSqlExpression[lambda.Parameters[0]] = aref; if(_outerNode) { // If this aggregate is basically the last/outer-most operator of the query // // produce SELECT Agg(exp) FROM seq // SqlExpression exp = (lambda != null) ? this.VisitExpression(lambda.Body) : null; SqlExpression where = null; if(isCount && exp != null) { where = exp; exp = null; } else if(exp == null && !isCount) { exp = aref; } if(exp != null) { // in case this contains another aggregate exp = new SqlSimpleExpression(exp); } SqlSelect sel = new SqlSelect( this.GetAggregate(aggType, returnType, exp), alias, _dominatingExpression ); sel.Where = where; sel.OrderingType = SqlOrderingType.Never; return sel; } else if(!isCount || lambda == null) { // Look to optimize aggregate by pushing its evaluation down to the select node that has the // actual group-by operator. // // Produce: SELECT Agg1 FROM (SELECT Agg(exp) as Agg1 FROM seq GROUP BY ...) // GroupInfo info = this.FindGroupInfo(source); if(info != null) { SqlExpression exp = null; if(lambda != null) { // evaluate expression relative to the group-by select node _parameterExpressionToSqlExpression[lambda.Parameters[0]] = (SqlExpression)SqlDuplicator.Copy(info.ElementOnGroupSource); exp = this.VisitExpression(lambda.Body); } else if(!isCount) { // support aggregates w/o an explicit selector specified exp = info.ElementOnGroupSource; } if(exp != null) { // in case this contains another aggregate exp = new SqlSimpleExpression(exp); } SqlExpression agg = this.GetAggregate(aggType, returnType, exp); SqlColumn c = new SqlColumn(agg.ClrType, agg.SqlType, null, null, agg, _dominatingExpression); info.SelectWithGroup.Row.Columns.Add(c); return new SqlColumnRef(c); } } // Otherwise, if we cannot optimize then fall back to generating a nested aggregate in a correlated sub query // // SCALAR(SELECT Agg(exp) FROM seq) { SqlExpression exp = (lambda != null) ? this.VisitExpression(lambda.Body) : null; if(exp != null) { // in case this contains another aggregate exp = new SqlSimpleExpression(exp); } SqlSelect sel = new SqlSelect( this.GetAggregate(aggType, returnType, isCount ? null : (lambda == null) ? aref : exp), alias, _dominatingExpression ); sel.Where = isCount ? exp : null; return _nodeFactory.SubSelect(SqlNodeType.ScalarSubSelect, sel); } }
private SqlNode VisitQuantifier(SqlSelect select, LambdaExpression lambda, bool isAny) { SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); if(lambda != null) { _parameterExpressionToSqlExpression[lambda.Parameters[0]] = aref; } SqlExpression cond = lambda != null ? this.VisitExpression(lambda.Body) : null; return this.GenerateQuantifier(alias, cond, isAny); }
private SqlSelect VisitOrderBy(Expression sequence, LambdaExpression expression, SqlOrderType orderType) { if(IsGrouping(expression.Body.Type)) { throw Error.GroupingNotSupportedAsOrderCriterion(); } if(!_typeProvider.From(expression.Body.Type).IsOrderable) { throw Error.TypeCannotBeOrdered(expression.Body.Type); } SqlSelect select = this.LockSelect(this.VisitSequence(sequence)); if(select.Selection.NodeType != SqlNodeType.AliasRef || select.OrderBy.Count > 0) { SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); select = new SqlSelect(aref, alias, _dominatingExpression); } _parameterExpressionToSqlExpression[expression.Parameters[0]] = (SqlAliasRef)select.Selection; SqlExpression expr = this.VisitExpression(expression.Body); select.OrderBy.Add(new SqlOrderExpression(orderType, expr)); return select; }
private SqlNode VisitGroupBy(Expression sequence, LambdaExpression keyLambda, LambdaExpression elemLambda, LambdaExpression resultSelector) { // Convert seq.Group(elem, key) into // // SELECT s.key, MULTISET(select s2.elem from seq AS s2 where s.key == s2.key) // FROM seq AS s // // where key and elem can be either simple scalars or object constructions // SqlSelect seq = this.VisitSequence(sequence); seq = this.LockSelect(seq); SqlAlias seqAlias = new SqlAlias(seq); SqlAliasRef seqAliasRef = new SqlAliasRef(seqAlias); // evaluate the key expression relative to original sequence _parameterExpressionToSqlExpression[keyLambda.Parameters[0]] = seqAliasRef; SqlExpression keyExpr = this.VisitExpression(keyLambda.Body); // make a duplicate of the original sequence to use as a foundation of our group multiset SqlDuplicator sd = new SqlDuplicator(); SqlSelect selDup = (SqlSelect)sd.Duplicate(seq); // rebind key in relative to the duplicate sequence SqlAlias selDupAlias = new SqlAlias(selDup); SqlAliasRef selDupRef = new SqlAliasRef(selDupAlias); _parameterExpressionToSqlExpression[keyLambda.Parameters[0]] = selDupRef; SqlExpression keyDup = this.VisitExpression(keyLambda.Body); SqlExpression elemExpr = null; SqlExpression elemOnGroupSource = null; if(elemLambda != null) { // evaluate element expression relative to the duplicate sequence _parameterExpressionToSqlExpression[elemLambda.Parameters[0]] = selDupRef; elemExpr = this.VisitExpression(elemLambda.Body); // evaluate element expression relative to original sequence _parameterExpressionToSqlExpression[elemLambda.Parameters[0]] = seqAliasRef; elemOnGroupSource = this.VisitExpression(elemLambda.Body); } else { // no elem expression supplied, so just use an alias ref to the duplicate sequence. // this will resolve to whatever was being produced by the sequence elemExpr = selDupRef; elemOnGroupSource = seqAliasRef; } // Make a sub expression out of the key. This will allow a single definition of the // expression to be shared at multiple points in the tree (via SqlSharedExpressionRef's) SqlSharedExpression keySubExpr = new SqlSharedExpression(keyExpr); keyExpr = new SqlSharedExpressionRef(keySubExpr); // construct the select clause that picks out the elements (this may be redundant...) SqlSelect selElem = new SqlSelect(elemExpr, selDupAlias, _dominatingExpression); selElem.Where = _nodeFactory.Binary(SqlNodeType.EQ2V, keyExpr, keyDup); // Finally, make the MULTISET node. this will be used as part of the final select SqlSubSelect ss = _nodeFactory.SubSelect(SqlNodeType.Multiset, selElem); // add a layer to the original sequence before applying the actual group-by clause SqlSelect gsel = new SqlSelect(new SqlSharedExpressionRef(keySubExpr), seqAlias, _dominatingExpression); gsel.GroupBy.Add(keySubExpr); SqlAlias gselAlias = new SqlAlias(gsel); SqlSelect result = null; if(resultSelector != null) { // Create final select to include construction of group multiset // select new Grouping { Key = key, Group = Multiset(select elem from seq where match) } from ... Type elementType = typeof(IGrouping<,>).MakeGenericType(keyExpr.ClrType, elemExpr.ClrType); SqlExpression keyGroup = new SqlGrouping(elementType, _typeProvider.From(elementType), keyExpr, ss, _dominatingExpression); SqlSelect keyGroupSel = new SqlSelect(keyGroup, gselAlias, _dominatingExpression); SqlAlias kgAlias = new SqlAlias(keyGroupSel); SqlAliasRef kgAliasRef = new SqlAliasRef(kgAlias); _parameterExpressionToSqlExpression[resultSelector.Parameters[0]] = _nodeFactory.Member(kgAliasRef, elementType.GetProperty("Key")); _parameterExpressionToSqlExpression[resultSelector.Parameters[1]] = kgAliasRef; // remember the select that has the actual group (for optimizing aggregates later) _sqlNodeToGroupInfo[kgAliasRef] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; SqlExpression resultExpr = this.VisitExpression(resultSelector.Body); result = new SqlSelect(resultExpr, kgAlias, _dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) _sqlNodeToGroupInfo[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; } else { // Create final select to include construction of group multiset // select new Grouping { Key = key, Group = Multiset(select elem from seq where match) } from ... Type elementType = typeof(IGrouping<,>).MakeGenericType(keyExpr.ClrType, elemExpr.ClrType); SqlExpression resultExpr = new SqlGrouping(elementType, _typeProvider.From(elementType), keyExpr, ss, _dominatingExpression); result = new SqlSelect(resultExpr, gselAlias, _dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) _sqlNodeToGroupInfo[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; } return result; }
private SqlSelect VisitDefaultIfEmpty(Expression sequence) { SqlSelect select = this.VisitSequence(sequence); SqlAlias alias = new SqlAlias(select); SqlAliasRef aliasRef = new SqlAliasRef(alias); SqlExpression opt = new SqlOptionalValue( new SqlColumn( "test", _nodeFactory.Unary(SqlNodeType.OuterJoinedValue, _nodeFactory.Value(typeof(int?), _typeProvider.From(typeof(int)), 1, false, _dominatingExpression) ) ), _nodeFactory.Unary(SqlNodeType.OuterJoinedValue, aliasRef) ); SqlSelect optSelect = new SqlSelect(opt, alias, _dominatingExpression); alias = new SqlAlias(optSelect); aliasRef = new SqlAliasRef(alias); SqlExpression litNull = _nodeFactory.TypedLiteralNull(typeof(string), _dominatingExpression); SqlSelect selNull = new SqlSelect(litNull, null, _dominatingExpression); SqlAlias aliasNull = new SqlAlias(selNull); SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, aliasNull, alias, null, _dominatingExpression); return new SqlSelect(aliasRef, join, _dominatingExpression); }
/// <summary> /// Given a ClientCase and a list of sequence (one for each case), construct a structure /// that is equivalent to a CASE of SELECTs. To accomplish this we use UNION ALL and attach /// a WHERE clause which will pick the SELECT that matches the discriminator in the Client Case. /// </summary> private SqlSelect SimulateCaseOfSequences(SqlClientCase clientCase, List<SqlNode> sequences) { /* * There are two situations we may be in: * (1) There is exactly one case alternative. * Here, no where clause is needed. * (2) There is more than case alternative. * Here, each WHERE clause needs to be ANDed with [Disc]=D where D * is the literal discriminanator value. */ if (sequences.Count == 1) { return (SqlSelect)sequences[0]; } else { SqlNode union = null; SqlSelect sel = null; int elseIndex = clientCase.Whens.Count - 1; int elseCount = clientCase.Whens[elseIndex].Match == null ? 1 : 0; SqlExpression elseFilter = null; for (int i = 0; i < sequences.Count - elseCount; ++i) { sel = (SqlSelect)sequences[i]; SqlExpression discriminatorPredicate = sql.Binary(SqlNodeType.EQ, clientCase.Expression, clientCase.Whens[i].Match); sel.Where = sql.AndAccumulate(sel.Where, discriminatorPredicate); elseFilter = sql.AndAccumulate(elseFilter, sql.Binary(SqlNodeType.NE, clientCase.Expression, clientCase.Whens[i].Match)); if (union == null) { union = sel; } else { union = new SqlUnion(sel, union, true /* Union All */); } } // Handle 'else' if present. if (elseCount == 1) { sel = (SqlSelect)sequences[elseIndex]; sel.Where = sql.AndAccumulate(sel.Where, elseFilter); if (union == null) { union = sel; } else { union = new SqlUnion(sel, union, true /* Union All */); } } SqlAlias alias = new SqlAlias(union); SqlAliasRef aref = new SqlAliasRef(alias); return new SqlSelect(aref, alias, union.SourceExpression); } }
private SqlSelect VisitSelect(Expression sequence, LambdaExpression selector) { SqlSelect source = this.VisitSequence(sequence); SqlAlias alias = new SqlAlias(source); SqlAliasRef aref = new SqlAliasRef(alias); _parameterExpressionToSqlExpression[selector.Parameters[0]] = aref; SqlNode project = this.Visit(selector.Body); SqlSelect pselect = project as SqlSelect; if(pselect != null) { return new SqlSelect(_nodeFactory.SubSelect(SqlNodeType.Multiset, pselect, selector.Body.Type), alias, _dominatingExpression); } else if((project.NodeType == SqlNodeType.Element || project.NodeType == SqlNodeType.ScalarSubSelect) && (_converterStrategy & ConverterStrategy.CanUseOuterApply) != 0) { SqlSubSelect sub = (SqlSubSelect)project; SqlSelect inner = sub.Select; SqlAlias innerAlias = new SqlAlias(inner); SqlAliasRef innerRef = new SqlAliasRef(innerAlias); if(project.NodeType == SqlNodeType.Element) { inner.Selection = new SqlOptionalValue( new SqlColumn( "test", _nodeFactory.Unary( SqlNodeType.OuterJoinedValue, _nodeFactory.Value(typeof(int?), _typeProvider.From(typeof(int)), 1, false, _dominatingExpression) ) ), _nodeFactory.Unary(SqlNodeType.OuterJoinedValue, inner.Selection) ); } else { inner.Selection = _nodeFactory.Unary(SqlNodeType.OuterJoinedValue, inner.Selection); } SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, alias, innerAlias, null, _dominatingExpression); return new SqlSelect(innerRef, join, _dominatingExpression); } else { SqlExpression expr = project as SqlExpression; if(expr != null) { return new SqlSelect(expr, alias, _dominatingExpression); } else { throw Error.BadProjectionInSelect(); } } }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { return this.ExpandExpression(aref); }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { if (this.UnwrapSequences != null && this.UnwrapSequences.Unwrap) { this.UnwrapSequences = new UnwrapStack(this.UnwrapSequences, false); this.VisitAlias(aref.Alias); this.UnwrapSequences = this.UnwrapSequences.Last; } else { this.VisitAlias(aref.Alias); } return aref; }
private SqlSelect LockSelect(SqlSelect sel) { if(sel.Selection.NodeType != SqlNodeType.AliasRef || sel.Where != null || sel.OrderBy.Count > 0 || sel.GroupBy.Count > 0 || sel.Having != null || sel.Top != null || sel.OrderingType != SqlOrderingType.Default || sel.IsDistinct) { SqlAlias alias = new SqlAlias(sel); SqlAliasRef aref = new SqlAliasRef(alias); return new SqlSelect(aref, alias, _dominatingExpression); } return sel; }
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); } }
private SqlExpression FoldSubquery(SqlSubSelect ss) { // convert ELEMENT(SELECT MULTISET(SELECT xxx FROM t1 WHERE p1) FROM t2 WHERE p2) // into MULTISET(SELECT xxx FROM t2 CA (SELECT xxx FROM t1 WHERE p1) WHERE p2)) while (true) { if (ss.NodeType == SqlNodeType.Element && ss.Select.Selection.NodeType == SqlNodeType.Multiset) { SqlSubSelect msub = (SqlSubSelect)ss.Select.Selection; SqlAlias alias = new SqlAlias(msub.Select); SqlAliasRef aref = new SqlAliasRef(alias); SqlSelect sel = ss.Select; sel.Selection = this.ConvertLinks(this.VisitExpression(aref)); sel.From = new SqlJoin(SqlJoinType.CrossApply, sel.From, alias, null, ss.SourceExpression); SqlSubSelect newss = sql.SubSelect(SqlNodeType.Multiset, sel, ss.ClrType); ss = newss; } else if (ss.NodeType == SqlNodeType.Element && ss.Select.Selection.NodeType == SqlNodeType.Element) { SqlSubSelect msub = (SqlSubSelect)ss.Select.Selection; SqlAlias alias = new SqlAlias(msub.Select); SqlAliasRef aref = new SqlAliasRef(alias); SqlSelect sel = ss.Select; sel.Selection = this.ConvertLinks(this.VisitExpression(aref)); sel.From = new SqlJoin(SqlJoinType.CrossApply, sel.From, alias, null, ss.SourceExpression); SqlSubSelect newss = sql.SubSelect(SqlNodeType.Element, sel); ss = newss; } else { break; } } return ss; }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { _commandStringBuilder.Append("AREF("); this.WriteAliasName(aref.Alias); _commandStringBuilder.Append(")"); return aref; }
private SqlExpression GenerateQuantifier(SqlAlias alias, SqlExpression cond, bool isAny) { SqlAliasRef aref = new SqlAliasRef(alias); if(isAny) { SqlSelect sel = new SqlSelect(aref, alias, _dominatingExpression); sel.Where = cond; sel.OrderingType = SqlOrderingType.Never; SqlSubSelect exists = _nodeFactory.SubSelect(SqlNodeType.Exists, sel); return exists; } else { SqlSelect sel = new SqlSelect(aref, alias, _dominatingExpression); SqlSubSelect ss = _nodeFactory.SubSelect(SqlNodeType.Exists, sel); sel.Where = _nodeFactory.Unary(SqlNodeType.Not2V, cond, _dominatingExpression); return _nodeFactory.Unary(SqlNodeType.Not, ss, _dominatingExpression); } }
private SqlSelect VisitJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) { SqlSelect outerSelect = this.VisitSequence(outerSequence); SqlSelect innerSelect = this.VisitSequence(innerSequence); SqlAlias outerAlias = new SqlAlias(outerSelect); SqlAliasRef outerRef = new SqlAliasRef(outerAlias); SqlAlias innerAlias = new SqlAlias(innerSelect); SqlAliasRef innerRef = new SqlAliasRef(innerAlias); _parameterExpressionToSqlExpression[outerKeySelector.Parameters[0]] = outerRef; SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body); _parameterExpressionToSqlExpression[innerKeySelector.Parameters[0]] = innerRef; SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body); _parameterExpressionToSqlExpression[resultSelector.Parameters[0]] = outerRef; _parameterExpressionToSqlExpression[resultSelector.Parameters[1]] = innerRef; SqlExpression result = this.VisitExpression(resultSelector.Body); SqlExpression condition = _nodeFactory.Binary(SqlNodeType.EQ, outerKey, innerKey); SqlSelect select = null; if((_converterStrategy & ConverterStrategy.CanUseJoinOn) != 0) { SqlJoin join = new SqlJoin(SqlJoinType.Inner, outerAlias, innerAlias, condition, _dominatingExpression); select = new SqlSelect(result, join, _dominatingExpression); } else { SqlJoin join = new SqlJoin(SqlJoinType.Cross, outerAlias, innerAlias, null, _dominatingExpression); select = new SqlSelect(result, join, _dominatingExpression); select.Where = condition; } return select; }
private SqlStatement VisitInsert(Expression item, LambdaExpression resultSelector) { if(item == null) { throw Error.ArgumentNull("item"); } _dominatingExpression = item; MetaTable metaTable = _services.Model.GetTable(item.Type); Expression source = _services.Context.GetTable(metaTable.RowType.Type).Expression; MetaType itemMetaType = null; SqlNew sqlItem = null; // construct insert assignments from 'item' info ConstantExpression conItem = item as ConstantExpression; if(conItem == null) { throw Error.InsertItemMustBeConstant(); } if(conItem.Value == null) { throw Error.ArgumentNull("item"); } // construct insert based on constant value List<SqlMemberAssign> bindings = new List<SqlMemberAssign>(); itemMetaType = metaTable.RowType.GetInheritanceType(conItem.Value.GetType()); SqlExpression sqlExprItem = _nodeFactory.ValueFromObject(conItem.Value, true, source); foreach(MetaDataMember mm in itemMetaType.PersistentDataMembers) { if(!mm.IsAssociation && !mm.IsDbGenerated && !mm.IsVersion) { bindings.Add(new SqlMemberAssign(mm.Member, _nodeFactory.Member(sqlExprItem, mm.Member))); } } ConstructorInfo cons = itemMetaType.Type.GetConstructor(Type.EmptyTypes); System.Diagnostics.Debug.Assert(cons != null); sqlItem = _nodeFactory.New(itemMetaType, cons, null, null, bindings, item); SqlTable tab = _nodeFactory.Table(metaTable, metaTable.RowType, _dominatingExpression); SqlInsert sin = new SqlInsert(tab, sqlItem, item); if(resultSelector == null) { return sin; } else { MetaDataMember id = itemMetaType.DBGeneratedIdentityMember; bool isDbGenOnly = false; if(id != null) { isDbGenOnly = this.IsDbGeneratedKeyProjectionOnly(resultSelector.Body, id); if(id.Type == typeof(Guid) && (_converterStrategy & ConverterStrategy.CanOutputFromInsert) != 0) { sin.OutputKey = new SqlColumn(id.Type, _nodeFactory.Default(id), id.Name, id, null, _dominatingExpression); if(!isDbGenOnly) { sin.OutputToLocal = true; } } } SqlSelect result = null; SqlSelect preResult = null; SqlAlias tableAlias = new SqlAlias(tab); SqlAliasRef tableAliasRef = new SqlAliasRef(tableAlias); System.Diagnostics.Debug.Assert(resultSelector.Parameters.Count == 1); _parameterExpressionToSqlExpression.Add(resultSelector.Parameters[0], tableAliasRef); SqlExpression projection = this.VisitExpression(resultSelector.Body); // build select to return result SqlExpression pred = null; if(id != null) { pred = _nodeFactory.Binary( SqlNodeType.EQ, _nodeFactory.Member(tableAliasRef, id.Member), this.GetIdentityExpression(id, sin.OutputKey != null) ); } else { SqlExpression itemExpression = this.VisitExpression(item); pred = _nodeFactory.Binary(SqlNodeType.EQ2V, tableAliasRef, itemExpression); } result = new SqlSelect(projection, tableAlias, resultSelector); result.Where = pred; // Since we're only projecting back a single generated key, we can // optimize the query to a simple selection (e.g. SELECT @@IDENTITY) // rather than selecting back from the table. if(id != null && isDbGenOnly) { if(sin.OutputKey == null) { SqlExpression exp = this.GetIdentityExpression(id, false); if(exp.ClrType != id.Type) { ProviderType sqlType = _nodeFactory.Default(id); exp = _nodeFactory.ConvertTo(id.Type, sqlType, exp); } // The result selector passed in was bound to the table - // we need to rebind to the single result as an array projection ParameterExpression p = Expression.Parameter(id.Type, "p"); Expression[] init = new Expression[1] { Expression.Convert(p, typeof(object)) }; NewArrayExpression arrExp = Expression.NewArrayInit(typeof(object), init); LambdaExpression rs = Expression.Lambda(arrExp, p); _parameterExpressionToSqlExpression.Add(p, exp); SqlExpression proj = this.VisitExpression(rs.Body); preResult = new SqlSelect(proj, null, rs); } else { // case handled in formatter automatically } result.DoNotOutput = true; } // combine insert & result into block SqlBlock block = new SqlBlock(_dominatingExpression); block.Statements.Add(sin); if(preResult != null) { block.Statements.Add(preResult); } block.Statements.Add(result); return block; } }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { throw Error.UnexpectedNode(aref.NodeType); }
private SqlSelect CoerceToSequence(SqlNode node) { SqlSelect select = node as SqlSelect; if(select == null) { if(node.NodeType == SqlNodeType.Value) { SqlValue sv = (SqlValue)node; // Check for ITables. ITable t = sv.Value as ITable; if(t != null) { return this.CoerceToSequence(this.TranslateConstantTable(t, null)); } // Check for IQueryable. IQueryable query = sv.Value as IQueryable; if(query != null) { Expression fex = Funcletizer.Funcletize(query.Expression); // IQueryables that return self-referencing Constant expressions cause infinite recursion if(fex.NodeType != ExpressionType.Constant || ((ConstantExpression)fex).Value != query) { return this.VisitSequence(fex); } throw Error.IQueryableCannotReturnSelfReferencingConstantExpression(); } throw Error.CapturedValuesCannotBeSequences(); } else if(node.NodeType == SqlNodeType.Multiset || node.NodeType == SqlNodeType.Element) { return ((SqlSubSelect)node).Select; } else if(node.NodeType == SqlNodeType.ClientArray) { throw Error.ConstructedArraysNotSupported(); } else if(node.NodeType == SqlNodeType.ClientParameter) { throw Error.ParametersCannotBeSequences(); } // this needs to be a sequence expression! SqlExpression sqlExpr = (SqlExpression)node; SqlAlias sa = new SqlAlias(sqlExpr); SqlAliasRef aref = new SqlAliasRef(sa); return new SqlSelect(aref, sa, _dominatingExpression); } return select; }
private SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) { SqlSelect select = this.LockSelect(sequence); // no skip? if(skipExp == null) { if(takeExp != null) { select.Top = takeExp; } return select; } SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); if(this.UseConverterStrategy(ConverterStrategy.SkipWithRowNumber)) { // use ROW_NUMBER() (preferred) SqlColumn rowNumber = new SqlColumn("ROW_NUMBER", _nodeFactory.RowNumber(new List<SqlOrderExpression>(), _dominatingExpression)); SqlColumnRef rowNumberRef = new SqlColumnRef(rowNumber); select.Row.Columns.Add(rowNumber); SqlSelect final = new SqlSelect(aref, alias, _dominatingExpression); if(takeExp != null) { // use BETWEEN for skip+take combo (much faster) final.Where = _nodeFactory.Between( rowNumberRef, _nodeFactory.Add(skipExp, 1), _nodeFactory.Binary(SqlNodeType.Add, (SqlExpression)SqlDuplicator.Copy(skipExp), takeExp), _dominatingExpression ); } else { final.Where = _nodeFactory.Binary(SqlNodeType.GT, rowNumberRef, skipExp); } return final; } else { // Ensure that the sequence contains elements that can be skipped if(!CanSkipOnSelection(select.Selection)) { throw Error.SkipNotSupportedForSequenceTypes(); } // use NOT EXISTS // Supported cases: // - Entities // - Projections that contain all PK columns // // .. where there sequence can be traced back to a: // - Single-table query // - Distinct // - Except // - Intersect // - Union, where union.All == false // Not supported: joins // Sequence should also be ordered, but we can't test for it at this // point in processing, and we won't know that we need to test it, later. SingleTableQueryVisitor stqv = new SingleTableQueryVisitor(); stqv.Visit(select); if(!stqv.IsValid) { throw Error.SkipRequiresSingleTableQueryWithPKs(); } SqlSelect dupsel = (SqlSelect)SqlDuplicator.Copy(select); dupsel.Top = skipExp; SqlAlias dupAlias = new SqlAlias(dupsel); SqlAliasRef dupRef = new SqlAliasRef(dupAlias); SqlSelect eqsel = new SqlSelect(dupRef, dupAlias, _dominatingExpression); eqsel.Where = _nodeFactory.Binary(SqlNodeType.EQ2V, aref, dupRef); SqlSubSelect ss = _nodeFactory.SubSelect(SqlNodeType.Exists, eqsel); SqlSelect final = new SqlSelect(aref, alias, _dominatingExpression); final.Where = _nodeFactory.Unary(SqlNodeType.Not, ss, _dominatingExpression); final.Top = takeExp; return final; } }
private SqlSelect VisitSelectMany(Expression sequence, LambdaExpression colSelector, LambdaExpression resultSelector) { SqlSelect seqSelect = this.VisitSequence(sequence); SqlAlias seqAlias = new SqlAlias(seqSelect); SqlAliasRef seqRef = new SqlAliasRef(seqAlias); _parameterExpressionToSqlExpression[colSelector.Parameters[0]] = seqRef; SqlNode colSelectorNode = this.VisitSequence(colSelector.Body); SqlAlias selAlias = new SqlAlias(colSelectorNode); SqlAliasRef selRef = new SqlAliasRef(selAlias); SqlJoin join = new SqlJoin(SqlJoinType.CrossApply, seqAlias, selAlias, null, _dominatingExpression); SqlExpression projection = selRef; if(resultSelector != null) { _parameterExpressionToSqlExpression[resultSelector.Parameters[0]] = seqRef; _parameterExpressionToSqlExpression[resultSelector.Parameters[1]] = selRef; projection = this.VisitExpression(resultSelector.Body); } return new SqlSelect(projection, join, _dominatingExpression); }
/// <summary> /// Translate a call to a table valued function expression into a sql select. /// </summary> private SqlNode TranslateTableValuedFunction(MethodCallExpression mce, MetaFunction function) { // translate method call into sql function call List<SqlExpression> sqlParams = GetFunctionParameters(mce, function); SqlTableValuedFunctionCall functionCall = _nodeFactory.TableValuedFunctionCall(function.ResultRowTypes[0].InheritanceRoot, mce.Method.ReturnType, function.MappedName, sqlParams, mce); SqlAlias alias = new SqlAlias(functionCall); SqlAliasRef aref = new SqlAliasRef(alias); // Build default projection SqlExpression projection = _translator.BuildProjection(aref, function.ResultRowTypes[0].InheritanceRoot, _allowDeferred, null, mce); SqlSelect select = new SqlSelect(projection, alias, mce); return select; }
private SqlSelect VisitGroupJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) { SqlSelect outerSelect = this.VisitSequence(outerSequence); SqlSelect innerSelect = this.VisitSequence(innerSequence); SqlAlias outerAlias = new SqlAlias(outerSelect); SqlAliasRef outerRef = new SqlAliasRef(outerAlias); SqlAlias innerAlias = new SqlAlias(innerSelect); SqlAliasRef innerRef = new SqlAliasRef(innerAlias); _parameterExpressionToSqlExpression[outerKeySelector.Parameters[0]] = outerRef; SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body); _parameterExpressionToSqlExpression[innerKeySelector.Parameters[0]] = innerRef; SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body); // make multiset SqlExpression pred = _nodeFactory.Binary(SqlNodeType.EQ, outerKey, innerKey); SqlSelect select = new SqlSelect(innerRef, innerAlias, _dominatingExpression); select.Where = pred; SqlSubSelect subquery = _nodeFactory.SubSelect(SqlNodeType.Multiset, select); // make outer ref & multiset for result-selector params _parameterExpressionToSqlExpression[resultSelector.Parameters[0]] = outerRef; _parameterExpressionToSqlNode[resultSelector.Parameters[1]] = subquery; SqlExpression result = this.VisitExpression(resultSelector.Body); return new SqlSelect(result, outerAlias, _dominatingExpression); }
private SqlNode VisitUnion(Expression source1, Expression source2) { SqlSelect left = this.VisitSequence(source1); SqlSelect right = this.VisitSequence(source2); SqlUnion union = new SqlUnion(left, right, false); SqlAlias alias = new SqlAlias(union); SqlAliasRef aref = new SqlAliasRef(alias); SqlSelect result = new SqlSelect(aref, alias, _dominatingExpression); result.OrderingType = SqlOrderingType.Blocked; return result; }
internal virtual SqlExpression VisitAliasRef(SqlAliasRef aref) { return aref; }
private SqlNode VisitExcept(Expression source1, Expression source2) { Type type = TypeSystem.GetElementType(source1.Type); if(IsGrouping(type)) { throw Error.ExceptNotSupportedForHierarchicalTypes(); } SqlSelect select1 = this.LockSelect(this.VisitSequence(source1)); SqlSelect select2 = this.VisitSequence(source2); SqlAlias alias1 = new SqlAlias(select1); SqlAliasRef aref1 = new SqlAliasRef(alias1); SqlAlias alias2 = new SqlAlias(select2); SqlAliasRef aref2 = new SqlAliasRef(alias2); SqlExpression any = this.GenerateQuantifier(alias2, _nodeFactory.Binary(SqlNodeType.EQ2V, aref1, aref2), true); SqlSelect result = new SqlSelect(aref1, alias1, select1.SourceExpression); result.Where = _nodeFactory.Unary(SqlNodeType.Not, any); result.IsDistinct = true; result.OrderingType = SqlOrderingType.Blocked; return result; }