private void GatherUnionExpressions(SqlNode node, List <SqlExpression> exprs) { SqlUnion union = node as SqlUnion; if (union != null) { this.GatherUnionExpressions(union.Left, exprs); this.GatherUnionExpressions(union.Right, exprs); } else { SqlSelect sel = node as SqlSelect; if (sel != null) { SqlAliasRef aref = sel.Selection as SqlAliasRef; if (aref != null) { this.GatherUnionExpressions(aref.Alias.Node, exprs); } else { exprs.Add(sel.Selection); } } } }
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); }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { if (this.ingoreExternalRefs && !this.nodeMap.ContainsKey(aref.Alias)) { return(aref); } return(new SqlAliasRef((SqlAlias)this.Visit(aref.Alias))); }
protected override SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) { //return base.GenerateSkipTake(sequence, skipExp, takeExp); SqlSelect node = LockSelect(sequence); var value2 = skipExp as SqlValue; if ((skipExp == null) || ((value2 != null) && (((int)value2.Value) <= 0))) { skipExp = sql.ValueFromObject(0, dominatingExpression); } var alias = new SqlAlias(node); var selection = new SqlAliasRef(alias); if (UseConverterStrategy(ConverterStrategy.SkipWithRowNumber)) { var col = new SqlColumn("ROW_NUMBER", this.sql.RowNumber(new List <SqlOrderExpression>(), this.dominatingExpression)); var expr = new SqlColumnRef(col); node.Row.Columns.Add(col); var select2 = new SqlSelect(selection, alias, this.dominatingExpression); if (takeExp != null) { select2.Where = this.sql.Between(expr, this.sql.Add(skipExp, 1), this.sql.Binary(SqlNodeType.Add, (SqlExpression)SqlDuplicator.Copy(skipExp), takeExp), this.dominatingExpression); return(select2); } select2.Where = this.sql.Binary(SqlNodeType.GT, expr, skipExp); return(select2); } if (!this.CanSkipOnSelection(node.Selection)) { throw SqlClient.Error.SkipNotSupportedForSequenceTypes(); } var visitor = new SingleTableQueryVisitor(); visitor.Visit(node); if (!visitor.IsValid) { throw ALinq.SqlClient.Error.SkipRequiresSingleTableQueryWithPKs(); } var select3 = (SqlSelect)SqlDuplicator.Copy(node); select3.Top = skipExp; var alias2 = new SqlAlias(select3); var ref4 = new SqlAliasRef(alias2); var select = new SqlSelect(ref4, alias2, this.dominatingExpression); select.Where = this.sql.Binary(SqlNodeType.EQ2V, selection, ref4); SqlSubSelect expression = this.sql.SubSelect(SqlNodeType.Exists, select); var select6 = new SqlSelect(selection, alias, this.dominatingExpression); select6.Where = this.sql.Unary(SqlNodeType.Not, expression, this.dominatingExpression); select6.Top = takeExp; return(select6); }
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) { if ((UnwrapSequences != null) && UnwrapSequences.Unwrap) { UnwrapSequences = new UnwrapStack(UnwrapSequences, false); VisitAlias(aref.Alias); UnwrapSequences = UnwrapSequences.Last; return(aref); } VisitAlias(aref.Alias); return(aref); }
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) { 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 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); }
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 SqlSelect VisitOrderBy(Expression sequence, LambdaExpression expression, SqlOrderType orderType) { if (IsGrouping(expression.Body.Type)) { throw Error.GroupingNotSupportedAsOrderCriterion(); } if (!this.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, this.dominatingExpression); } this.map[expression.Parameters[0]] = (SqlAliasRef)select.Selection; SqlExpression expr = this.VisitExpression(expression.Body); select.OrderBy.Add(new SqlOrderExpression(orderType, expr)); return select; }
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, this.dominatingExpression); result.OrderingType = SqlOrderingType.Blocked; return result; }
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, sql.Binary(SqlNodeType.EQ2V, aref1, aref2), true); SqlSelect result = new SqlSelect(aref1, alias1, select1.SourceExpression); result.Where = sql.Unary(SqlNodeType.Not, any); result.IsDistinct = true; result.OrderingType = SqlOrderingType.Blocked; return result; }
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", sql.RowNumber(new List<SqlOrderExpression>(), this.dominatingExpression)); SqlColumnRef rowNumberRef = new SqlColumnRef(rowNumber); select.Row.Columns.Add(rowNumber); SqlSelect final = new SqlSelect(aref, alias, this.dominatingExpression); if (takeExp != null) { // use BETWEEN for skip+take combo (much faster) final.Where = sql.Between( rowNumberRef, sql.Add(skipExp, 1), sql.Binary(SqlNodeType.Add, (SqlExpression)SqlDuplicator.Copy(skipExp), takeExp), this.dominatingExpression ); } else { final.Where = sql.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, this.dominatingExpression); eqsel.Where = sql.Binary(SqlNodeType.EQ2V, aref, dupRef); SqlSubSelect ss = sql.SubSelect(SqlNodeType.Exists, eqsel); SqlSelect final = new SqlSelect(aref, alias, this.dominatingExpression); final.Where = sql.Unary(SqlNodeType.Not, ss, this.dominatingExpression); final.Top = takeExp; return final; } }
/// <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 = sql.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 = this.translator.BuildProjection(aref, function.ResultRowTypes[0].InheritanceRoot, this.allowDeferred, null, mce); SqlSelect select = new SqlSelect(projection, alias, mce); return select; }
private SqlNode VisitQuantifier(SqlSelect select, LambdaExpression lambda, bool isAny) { SqlAlias alias = new SqlAlias(select); SqlAliasRef aref = new SqlAliasRef(alias); if (lambda != null) { this.map[lambda.Parameters[0]] = aref; } SqlExpression cond = lambda != null ? this.VisitExpression(lambda.Body) : null; return this.GenerateQuantifier(alias, cond, isAny); }
protected override SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) { if (skipExp == null) { return(base.GenerateSkipTake(sequence, skipExp, takeExp)); } Debug.Assert(skipExp != null); var skipCount = (int)((SqlValue)skipExp).Value; var alias = new SqlAlias(sequence); var selection = new SqlAliasRef(alias); var select = new SqlSelect(selection, alias, dominatingExpression); if (takeExp == null) { return(base.GenerateSkipTake(sequence, skipExp, takeExp)); } var type = sequence.SourceExpression.Type; if (type.IsGenericType) { var args = type.GetGenericArguments(); if (args.Length == 1) { var mappingTable = Services.Model.GetTable(args[0]); if (mappingTable != null) { return(base.GenerateSkipTake(sequence, skipExp, takeExp)); } } } var takeCount = (int)((SqlValue)takeExp).Value; select.Top = sql.Value(typeof(int), typeProvider.From(typeof(int)), takeCount, false, dominatingExpression); sequence.Top = sql.Value(typeof(int), typeProvider.From(typeof(int)), takeCount + skipCount, false, dominatingExpression); var finder = new OrderByFinder(); var orderby = finder.GetOrderBy(sequence); foreach (var item in orderby) { if (item.OrderType == SqlOrderType.Descending) { select.OrderBy.Add(new SqlOrderExpression(SqlOrderType.Ascending, orderby[0].Expression)); } else { select.OrderBy.Add(new SqlOrderExpression(SqlOrderType.Descending, orderby[0].Expression)); } } alias = new SqlAlias(select); selection = new SqlAliasRef(alias); select = new SqlSelect(selection, alias, dominatingExpression); foreach (var item in orderby) { select.OrderBy.Add(new SqlOrderExpression(item.OrderType, item.Expression)); } 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); this.map[outerKeySelector.Parameters[0]] = outerRef; SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body); this.map[innerKeySelector.Parameters[0]] = innerRef; SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body); // make multiset SqlExpression pred = sql.Binary(SqlNodeType.EQ, outerKey, innerKey); SqlSelect select = new SqlSelect(innerRef, innerAlias, this.dominatingExpression); select.Where = pred; SqlSubSelect subquery = sql.SubSelect(SqlNodeType.Multiset, select); // make outer ref & multiset for result-selector params this.map[resultSelector.Parameters[0]] = outerRef; this.dupMap[resultSelector.Parameters[1]] = subquery; SqlExpression result = this.VisitExpression(resultSelector.Body); return new SqlSelect(result, outerAlias, this.dominatingExpression); }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { if (this.ingoreExternalRefs && !this.nodeMap.ContainsKey(aref.Alias)) { return aref; } return new SqlAliasRef((SqlAlias)this.Visit(aref.Alias)); }
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, this.dominatingExpression); } return select; }
private SqlSelect VisitSelectMany(Expression sequence, LambdaExpression colSelector, LambdaExpression resultSelector) { SqlSelect seqSelect = this.VisitSequence(sequence); SqlAlias seqAlias = new SqlAlias(seqSelect); SqlAliasRef seqRef = new SqlAliasRef(seqAlias); this.map[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, this.dominatingExpression); SqlExpression projection = selRef; if (resultSelector != null) { this.map[resultSelector.Parameters[0]] = seqRef; this.map[resultSelector.Parameters[1]] = selRef; projection = this.VisitExpression(resultSelector.Body); } return new SqlSelect(projection, join, this.dominatingExpression); }
private SqlStatement VisitInsert(Expression item, LambdaExpression resultSelector) { if (item == null) { throw Error.ArgumentNull("item"); } this.dominatingExpression = item; MetaTable metaTable = this.services.Model.GetTable(item.Type); Expression source = this.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 = sql.ValueFromObject(conItem.Value, true, source); foreach (MetaDataMember mm in itemMetaType.PersistentDataMembers) { if (!mm.IsAssociation && !mm.IsDbGenerated && !mm.IsVersion) { bindings.Add(new SqlMemberAssign(mm.Member, sql.Member(sqlExprItem, mm.Member))); } } ConstructorInfo cons = itemMetaType.Type.GetConstructor(Type.EmptyTypes); System.Diagnostics.Debug.Assert(cons != null); sqlItem = sql.New(itemMetaType, cons, null, null, bindings, item); SqlTable tab = sql.Table(metaTable, metaTable.RowType, this.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) && (this.converterStrategy & ConverterStrategy.CanOutputFromInsert) != 0) { sin.OutputKey = new SqlColumn(id.Type, sql.Default(id), id.Name, id, null, this.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); this.map.Add(resultSelector.Parameters[0], tableAliasRef); SqlExpression projection = this.VisitExpression(resultSelector.Body); // build select to return result SqlExpression pred = null; if (id != null) { pred = sql.Binary( SqlNodeType.EQ, sql.Member(tableAliasRef, id.Member), this.GetIdentityExpression(id, sin.OutputKey != null) ); } else { SqlExpression itemExpression = this.VisitExpression(item); pred = sql.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 = sql.Default(id); exp = sql.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); this.map.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(this.dominatingExpression); block.Statements.Add(sin); if (preResult != null) { block.Statements.Add(preResult); } block.Statements.Add(result); return block; } }
internal virtual SqlExpression VisitAliasRef(SqlAliasRef aref) { return aref; }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { throw Error.UnexpectedNode(aref.NodeType); }
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 this.map[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); this.map[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 this.map[elemLambda.Parameters[0]] = selDupRef; elemExpr = this.VisitExpression(elemLambda.Body); // evaluate element expression relative to original sequence this.map[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, this.dominatingExpression); selElem.Where = sql.Binary(SqlNodeType.EQ2V, keyExpr, keyDup); // Finally, make the MULTISET node. this will be used as part of the final select SqlSubSelect ss = sql.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, this.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, this.typeProvider.From(elementType), keyExpr, ss, this.dominatingExpression); SqlSelect keyGroupSel = new SqlSelect(keyGroup, gselAlias, this.dominatingExpression); SqlAlias kgAlias = new SqlAlias(keyGroupSel); SqlAliasRef kgAliasRef = new SqlAliasRef(kgAlias); this.map[resultSelector.Parameters[0]] = sql.Member(kgAliasRef, elementType.GetProperty("Key")); this.map[resultSelector.Parameters[1]] = kgAliasRef; // remember the select that has the actual group (for optimizing aggregates later) this.gmap[kgAliasRef] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; SqlExpression resultExpr = this.VisitExpression(resultSelector.Body); result = new SqlSelect(resultExpr, kgAlias, this.dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) this.gmap[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, this.typeProvider.From(elementType), keyExpr, ss, this.dominatingExpression); result = new SqlSelect(resultExpr, gselAlias, this.dominatingExpression); // remember the select that has the actual group (for optimizing aggregates later) this.gmap[resultExpr] = new GroupInfo { SelectWithGroup = gsel, ElementOnGroupSource = elemOnGroupSource }; } return result; }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { throw Error.UnexpectedNode(aref.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) this.map[lambda.Parameters[0]] = aref; if (this.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, this.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 this.map[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, this.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, this.dominatingExpression ); sel.Where = isCount ? exp : null; return sql.SubSelect(SqlNodeType.ScalarSubSelect, sel); } }
internal SqlExpression TranslateEquals(SqlBinary expr) { System.Diagnostics.Debug.Assert( expr.NodeType == SqlNodeType.EQ || expr.NodeType == SqlNodeType.NE || expr.NodeType == SqlNodeType.EQ2V || expr.NodeType == SqlNodeType.NE2V); SqlExpression eLeft = expr.Left; SqlExpression eRight = expr.Right; if (eRight.NodeType == SqlNodeType.Element) { SqlSubSelect sub = (SqlSubSelect)eRight; SqlAlias alias = new SqlAlias(sub.Select); SqlAliasRef aref = new SqlAliasRef(alias); SqlSelect select = new SqlSelect(aref, alias, expr.SourceExpression); select.Where = sql.Binary(expr.NodeType, sql.DoNotVisitExpression(eLeft), aref); return(sql.SubSelect(SqlNodeType.Exists, select)); } else if (eLeft.NodeType == SqlNodeType.Element) { SqlSubSelect sub = (SqlSubSelect)eLeft; SqlAlias alias = new SqlAlias(sub.Select); SqlAliasRef aref = new SqlAliasRef(alias); SqlSelect select = new SqlSelect(aref, alias, expr.SourceExpression); select.Where = sql.Binary(expr.NodeType, sql.DoNotVisitExpression(eRight), aref); return(sql.SubSelect(SqlNodeType.Exists, select)); } MetaType mtLeft = TypeSource.GetSourceMetaType(eLeft, this.services.Model); MetaType mtRight = TypeSource.GetSourceMetaType(eRight, this.services.Model); if (eLeft.NodeType == SqlNodeType.TypeCase) { eLeft = BestIdentityNode((SqlTypeCase)eLeft); } if (eRight.NodeType == SqlNodeType.TypeCase) { eRight = BestIdentityNode((SqlTypeCase)eRight); } if (mtLeft.IsEntity && mtRight.IsEntity && mtLeft.Table != mtRight.Table) { throw Error.CannotCompareItemsAssociatedWithDifferentTable(); } // do simple or no translation for non-structural types if (!mtLeft.IsEntity && !mtRight.IsEntity && (eLeft.NodeType != SqlNodeType.New || eLeft.SqlType.CanBeColumn) && (eRight.NodeType != SqlNodeType.New || eRight.SqlType.CanBeColumn)) { if (expr.NodeType == SqlNodeType.EQ2V || expr.NodeType == SqlNodeType.NE2V) { return(this.TranslateEqualsOp(expr.NodeType, sql.DoNotVisitExpression(expr.Left), sql.DoNotVisitExpression(expr.Right), false)); } return(expr); } // If the two types are not comparable, we return the predicate "1=0". if ((mtLeft != mtRight) && (mtLeft.InheritanceRoot != mtRight.InheritanceRoot)) { return(sql.Binary(SqlNodeType.EQ, sql.ValueFromObject(0, expr.SourceExpression), sql.ValueFromObject(1, expr.SourceExpression))); } List <SqlExpression> exprs1; List <SqlExpression> exprs2; SqlLink link1 = eLeft as SqlLink; if (link1 != null && link1.Member.IsAssociation && link1.Member.Association.IsForeignKey) { exprs1 = link1.KeyExpressions; } else { exprs1 = this.GetIdentityExpressions(mtLeft, sql.DoNotVisitExpression(eLeft)); } SqlLink link2 = eRight as SqlLink; if (link2 != null && link2.Member.IsAssociation && link2.Member.Association.IsForeignKey) { exprs2 = link2.KeyExpressions; } else { exprs2 = this.GetIdentityExpressions(mtRight, sql.DoNotVisitExpression(eRight)); } System.Diagnostics.Debug.Assert(exprs1.Count > 0); System.Diagnostics.Debug.Assert(exprs2.Count > 0); System.Diagnostics.Debug.Assert(exprs1.Count == exprs2.Count); SqlExpression exp = null; SqlNodeType eqKind = (expr.NodeType == SqlNodeType.EQ2V || expr.NodeType == SqlNodeType.NE2V) ? SqlNodeType.EQ2V : SqlNodeType.EQ; for (int i = 0, n = exprs1.Count; i < n; i++) { SqlExpression eq = this.TranslateEqualsOp(eqKind, exprs1[i], exprs2[i], !mtLeft.IsEntity); if (exp == null) { exp = eq; } else { exp = sql.Binary(SqlNodeType.And, exp, eq); } } if (expr.NodeType == SqlNodeType.NE || expr.NodeType == SqlNodeType.NE2V) { exp = sql.Unary(SqlNodeType.Not, exp, exp.SourceExpression); } return(exp); }
private SqlExpression GenerateQuantifier(SqlAlias alias, SqlExpression cond, bool isAny) { SqlAliasRef aref = new SqlAliasRef(alias); if (isAny) { SqlSelect sel = new SqlSelect(aref, alias, this.dominatingExpression); sel.Where = cond; sel.OrderingType = SqlOrderingType.Never; SqlSubSelect exists = sql.SubSelect(SqlNodeType.Exists, sel); return exists; } else { SqlSelect sel = new SqlSelect(aref, alias, this.dominatingExpression); SqlSubSelect ss = sql.SubSelect(SqlNodeType.Exists, sel); sel.Where = sql.Unary(SqlNodeType.Not2V, cond, this.dominatingExpression); return sql.Unary(SqlNodeType.Not, ss, this.dominatingExpression); } }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { SqlAlias alias = aref.Alias; SqlAlias value; if (this.removedMap.TryGetValue(alias, out value)) { throw Error.InvalidReferenceToRemovedAliasDuringDeflation(); } 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, this.dominatingExpression); } return sel; }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { return this.ExpandExpression(aref); }
private SqlSelect VisitSelect(Expression sequence, LambdaExpression selector) { SqlSelect source = this.VisitSequence(sequence); SqlAlias alias = new SqlAlias(source); SqlAliasRef aref = new SqlAliasRef(alias); this.map[selector.Parameters[0]] = aref; SqlNode project = this.Visit(selector.Body); SqlSelect pselect = project as SqlSelect; if (pselect != null) { return new SqlSelect(sql.SubSelect(SqlNodeType.Multiset, pselect, selector.Body.Type), alias, this.dominatingExpression); } else if ((project.NodeType == SqlNodeType.Element || project.NodeType == SqlNodeType.ScalarSubSelect) && (this.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", sql.Unary( SqlNodeType.OuterJoinedValue, sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression) ) ), sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection) ); } else { inner.Selection = sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection); } SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, alias, innerAlias, null, this.dominatingExpression); return new SqlSelect(innerRef, join, this.dominatingExpression); } else { SqlExpression expr = project as SqlExpression; if (expr != null) { return new SqlSelect(expr, alias, this.dominatingExpression); } else { throw Error.BadProjectionInSelect(); } } }
internal SqlExpression ConvertToFetchedExpression(SqlNode node) { if (node == null) { return null; } switch (node.NodeType) { case SqlNodeType.OuterJoinedValue: { SqlExpression o = ((SqlUnary)node).Operand; SqlExpression e = this.ConvertLinks(o); if (e == o) { return (SqlExpression)node; } return e; } case SqlNodeType.ClientCase: { // Need to recurse in case the object case has links. SqlClientCase cc = (SqlClientCase)node; List<SqlNode> fetchedValues = new List<SqlNode>(); bool allExprs = true; foreach (SqlClientWhen when in cc.Whens) { SqlNode fetchedValue = ConvertToFetchedExpression(when.Value); allExprs = allExprs && (fetchedValue is SqlExpression); fetchedValues.Add(fetchedValue); } if (allExprs) { // All WHEN values are simple expressions (no sequences). List<SqlExpression> matches = new List<SqlExpression>(); List<SqlExpression> values = new List<SqlExpression>(); for (int i = 0, c = fetchedValues.Count; i < c; ++i) { SqlExpression fetchedValue = (SqlExpression)fetchedValues[i]; if (!cc.ClrType.IsAssignableFrom(fetchedValue.ClrType)) { throw Error.DidNotExpectTypeChange(cc.ClrType, fetchedValue.ClrType); } matches.Add(cc.Whens[i].Match); values.Add(fetchedValue); } node = sql.Case(cc.ClrType, cc.Expression, matches, values, cc.SourceExpression); } else { node = SimulateCaseOfSequences(cc, fetchedValues); } break; } case SqlNodeType.TypeCase: { SqlTypeCase tc = (SqlTypeCase)node; List<SqlNode> fetchedValues = new List<SqlNode>(); foreach (SqlTypeCaseWhen when in tc.Whens) { SqlNode fetchedValue = ConvertToFetchedExpression(when.TypeBinding); fetchedValues.Add(fetchedValue); } for (int i = 0, c = fetchedValues.Count; i < c; ++i) { SqlExpression fetchedValue = (SqlExpression)fetchedValues[i]; tc.Whens[i].TypeBinding = fetchedValue; } break; } case SqlNodeType.SearchedCase: { SqlSearchedCase sc = (SqlSearchedCase)node; foreach (SqlWhen when in sc.Whens) { when.Match = this.ConvertToFetchedExpression(when.Match); when.Value = this.ConvertToFetchedExpression(when.Value); } sc.Else = this.ConvertToFetchedExpression(sc.Else); break; } case SqlNodeType.Link: { SqlLink link = (SqlLink)node; if (link.Expansion != null) { return this.VisitLinkExpansion(link); } SqlExpression cached; if (this.linkMap.TryGetValue(link.Id, out cached)) { return this.VisitExpression(cached); } // translate link into expanded form node = this.translator.TranslateLink(link, true); // New nodes may have been produced because of Subquery. // Prebind again for method-call and static treat handling. node = binder.Prebind(node); // Make it an expression. node = this.ConvertToExpression(node); // bind the translation node = this.Visit(node); // Check for element node, rewrite as sql apply. if (this.currentSelect != null && node != null && node.NodeType == SqlNodeType.Element && link.Member.IsAssociation && this.binder.OptimizeLinkExpansions ) { // if link in a non-nullable foreign key association then inner-join is okay to use (since it must always exist) // otherwise use left-outer-join SqlJoinType joinType = (link.Member.Association.IsForeignKey && !link.Member.Association.IsNullable) ? SqlJoinType.Inner : SqlJoinType.LeftOuter; SqlSubSelect ss = (SqlSubSelect)node; SqlExpression where = ss.Select.Where; ss.Select.Where = null; // form cross apply SqlAlias sa = new SqlAlias(ss.Select); if (joinType == SqlJoinType.Inner && this.IsOuterDependent(this.currentSelect.From, sa, where)) { joinType = SqlJoinType.LeftOuter; } this.currentSelect.From = sql.MakeJoin(joinType, this.currentSelect.From, sa, where, ss.SourceExpression); SqlExpression result = new SqlAliasRef(sa); this.linkMap.Add(link.Id, result); return this.VisitExpression(result); } } break; } return (SqlExpression)node; }
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); this.map[outerKeySelector.Parameters[0]] = outerRef; SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body); this.map[innerKeySelector.Parameters[0]] = innerRef; SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body); this.map[resultSelector.Parameters[0]] = outerRef; this.map[resultSelector.Parameters[1]] = innerRef; SqlExpression result = this.VisitExpression(resultSelector.Body); SqlExpression condition = sql.Binary(SqlNodeType.EQ, outerKey, innerKey); SqlSelect select = null; if ((this.converterStrategy & ConverterStrategy.CanUseJoinOn) != 0) { SqlJoin join = new SqlJoin(SqlJoinType.Inner, outerAlias, innerAlias, condition, this.dominatingExpression); select = new SqlSelect(result, join, this.dominatingExpression); } else { SqlJoin join = new SqlJoin(SqlJoinType.Cross, outerAlias, innerAlias, null, this.dominatingExpression); select = new SqlSelect(result, join, this.dominatingExpression); select.Where = condition; } return select; }
/// <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 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", sql.Unary(SqlNodeType.OuterJoinedValue, sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression) ) ), sql.Unary(SqlNodeType.OuterJoinedValue, aliasRef) ); SqlSelect optSelect = new SqlSelect(opt, alias, this.dominatingExpression); alias = new SqlAlias(optSelect); aliasRef = new SqlAliasRef(alias); SqlExpression litNull = sql.TypedLiteralNull(typeof(string), this.dominatingExpression); SqlSelect selNull = new SqlSelect(litNull, null, this.dominatingExpression); SqlAlias aliasNull = new SqlAlias(selNull); SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, aliasNull, alias, null, this.dominatingExpression); return new SqlSelect(aliasRef, join, this.dominatingExpression); }
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); System.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); System.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); System.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 virtual SqlExpression VisitAliasRef(SqlAliasRef aref) { return aref; }
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) { sb.Append("AREF("); this.WriteAliasName(aref.Alias); sb.Append(")"); return 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; }