public void Count_StringViewIsValid() { Assert.Equal("COUNT(pe.Name) AS TestAlias", SqlField <Person> .Count(p => p.Name, "TestAlias").ToString()); var alias = new SqlAlias <Passport>("t"); Assert.Equal("COUNT(t.Number) AS Passp", SqlField <Passport> .Count(alias, p => p.Number, "Passp").ToString()); }
private SqlJoin GetLeftOuterWithUnreferencedSingletonOnLeft(SqlSource source) { SqlAlias alias = source as SqlAlias; if (alias != null) { SqlSelect select = alias.Node as SqlSelect; if (select != null && select.Where == null && select.Top == null && select.GroupBy.Count == 0 && select.OrderBy.Count == 0) { return(this.GetLeftOuterWithUnreferencedSingletonOnLeft(select.From)); } } SqlJoin join = source as SqlJoin; if (join == null || join.JoinType != SqlJoinType.LeftOuter) { return(null); } if (!this.IsSingletonSelect(join.Left)) { return(null); } HashSet <SqlAlias> p = SqlGatherProducedAliases.Gather(join.Left); HashSet <SqlAlias> c = SqlGatherConsumedAliases.Gather(join.Right); if (p.Overlaps(c)) { return(null); } return(join); }
public void From_StringViewIsValid() { Assert.Equal("pe.Name AS TestAlias", SqlField <Person> .From(p => p.Name, "TestAlias").ToString()); var alias = new SqlAlias <Passport>("t"); Assert.Equal("t.Number AS Passp", SqlField <Passport> .From(alias, p => p.Number, "Passp").ToString()); }
internal SqlAlias VisitAliasConsumed(SqlAlias a) { if (a == null) { return(a); } bool match = false; foreach (SqlAlias alias in aliases) { if (alias == a) { match = true; break; } } if (match) { this.referencesAnyMatchingAliases = true; } return(a); }
internal override SqlAlias VisitAlias(SqlAlias sqlAlias) { SqlAlias save = this.alias; this.alias = sqlAlias; sqlAlias.Node = this.Visit(sqlAlias.Node); this.alias = save; return sqlAlias; }
internal override SqlAlias VisitAlias(SqlAlias a) { SqlAlias n = new SqlAlias(a.Node); this.nodeMap[a] = n; n.Node = this.Visit(a.Node); n.Name = a.Name; return n; }
internal override SqlExpression VisitMultiset(SqlSubSelect sms) { // allow one big-join per query? if ((this.options & SqlMultiplexerOptionType.EnableBigJoin) != 0 && !this.hasBigJoin && this.canJoin && this.isTopLevel && this.outerSelect != null && !MultisetChecker.HasMultiset(sms.Select.Selection) && BigJoinChecker.CanBigJoin(sms.Select)) { sms.Select = this.VisitSelect(sms.Select); SqlAlias alias = new SqlAlias(sms.Select); SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, this.outerSelect.From, alias, null, sms.SourceExpression); this.outerSelect.From = @join; this.outerSelect.OrderingType = SqlOrderingType.Always; // make joined expression SqlExpression expr = (SqlExpression)SqlDuplicator.Copy(sms.Select.Selection); // make count expression SqlSelect copySelect = (SqlSelect)SqlDuplicator.Copy(sms.Select); SqlAlias copyAlias = new SqlAlias(copySelect); SqlSelect countSelect = new SqlSelect(sql.Unary(SqlNodeType.Count, null, sms.SourceExpression), copyAlias, sms.SourceExpression); countSelect.OrderingType = SqlOrderingType.Never; SqlExpression count = sql.SubSelect(SqlNodeType.ScalarSubSelect, countSelect); // make joined collection SqlJoinedCollection jc = new SqlJoinedCollection(sms.ClrType, sms.SqlType, expr, count, sms.SourceExpression); this.hasBigJoin = true; return(jc); } return(QueryExtractor.Extract(sms, this.parentParameters)); }
internal override SqlExpression VisitMultiset(SqlSubSelect sms) { // allow one big-join per query? if ((this.options & Options.EnableBigJoin) != 0 && !this.hasBigJoin && this.canJoin && this.isTopLevel && this.outerSelect != null && !MultisetChecker.HasMultiset(sms.Select.Selection) && BigJoinChecker.CanBigJoin(sms.Select)) { sms.Select = this.VisitSelect(sms.Select); SqlAlias alias = new SqlAlias(sms.Select); SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, this.outerSelect.From, alias, null, sms.SourceExpression); this.outerSelect.From = join; this.outerSelect.OrderingType = SqlOrderingType.Always; // make joined expression SqlExpression expr = (SqlExpression)SqlDuplicator.Copy(sms.Select.Selection); // make count expression SqlSelect copySelect = (SqlSelect)SqlDuplicator.Copy(sms.Select); SqlAlias copyAlias = new SqlAlias(copySelect); SqlSelect countSelect = new SqlSelect(sql.Unary(SqlNodeType.Count, null, sms.SourceExpression), copyAlias, sms.SourceExpression); countSelect.OrderingType = SqlOrderingType.Never; SqlExpression count = sql.SubSelect(SqlNodeType.ScalarSubSelect, countSelect); // make joined collection SqlJoinedCollection jc = new SqlJoinedCollection(sms.ClrType, sms.SqlType, expr, count, sms.SourceExpression); this.hasBigJoin = true; return jc; } else { return QueryExtractor.Extract(sms, this.parentParameters); } }
protected override SqlExpression VisitAlias(SqlAlias alias) { bool isSelect = (alias.Expression is SqlSelect); int depth = _depth; string name = String.Empty; string aliasName = null; SqlField field = (alias.Expression as SqlField); if (field != null) { name = field.Name; } SqlTable table = (alias.Expression as SqlTable); if (table != null) { name = table.Name; } if (alias.Name == null) { if (!_nameMap.TryGetValue(alias, out aliasName)) { aliasName = "A" + _nameMap.Count; _nameMap[alias] = aliasName; } } else { aliasName = alias.Name; } if (isSelect) { _depth++; _builder.Append("("); this.NewLine(); } Visit(alias.Expression); if (isSelect) { this.NewLine(); _builder.Append(")"); _depth = depth; } if ((!_suppressedAliases.Contains(alias) && aliasName != null) && name != aliasName) { _builder.Append(" AS "); WriteName(aliasName); } return(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 SqlAlias VisitAlias(SqlAlias a) { SqlAlias n = new SqlAlias(a.Node); this.nodeMap[a] = n; n.Node = this.Visit(a.Node); n.Name = a.Name; return(n); }
internal override SqlAlias VisitAlias(SqlAlias sqlAlias) { SqlAlias save = this.alias; this.alias = sqlAlias; sqlAlias.Node = this.Visit(sqlAlias.Node); this.alias = save; return(sqlAlias); }
internal SqlAliasRef(SqlAlias alias) : base(SqlNodeType.AliasRef, GetClrType(alias.Node), alias.SourceExpression) { if (alias == null) { throw Error.ArgumentNull("alias"); } this.alias = alias; }
internal override SqlAlias VisitAlias(SqlAlias a) { SqlAlias save = _currentAlias; _currentAlias = a; base.VisitAlias(a); _currentAlias = save; return(a); }
internal override SqlExpression VisitScalarSubSelect(SqlSubSelect ss) { SqlSelect innerSelect = this.VisitSelect(ss.Select); if (!this.aggregateChecker.HasAggregates(innerSelect)) { innerSelect.Top = this.sql.ValueFromObject(1, ss.SourceExpression); } innerSelect.OrderingType = SqlOrderingType.Blocked; SqlAlias alias = new SqlAlias(innerSelect); this.currentSelect.From = new SqlJoin(SqlJoinType.OuterApply, this.currentSelect.From, alias, null, ss.SourceExpression); return new SqlColumnRef(innerSelect.Row.Columns[0]); }
public void FullJoin() { var alias = new SqlAlias <Passport>("pas"); var expected = @"FULL JOIN Passport pas ON pe.Id = pas.PersonId"; Assert.Equal(expected, new SqlJoin <Passport>(JoinType.Full, SqlFilter <Person> .From(p => p.Id).EqualTo <Passport>(p => p.PersonId, alias), alias).ToString()); }
public void InnerJoin() { var alias = new SqlAlias <Passport>("pas"); var expected = @"INNER JOIN Passport pas ON pas.PersonId = pe.Id"; Assert.Equal(expected, new SqlJoin <Passport>(JoinType.Inner, SqlFilter <Passport> .From(p => p.PersonId, alias).EqualTo <Person>(p => p.Id), alias).ToString()); }
private bool HasTrivialSource(SqlSource node) { SqlAlias alias = node as SqlAlias; if (alias == null) { return(false); } return(alias.Node is SqlSelect); }
internal override SqlExpression VisitAliasRef(SqlAliasRef aref) { SqlAlias alias = aref.Alias; SqlAlias value; if (_removedMap.TryGetValue(alias, out value)) { throw Error.InvalidReferenceToRemovedAliasDuringDeflation(); } return(aref); }
public void InnerJoinWithLongFilter() { var alias = new SqlAlias <Passport>("pas"); var expected = @"INNER JOIN Passport pas ON pas.PersonId = pe.Id AND pa.Number IS NOT NULL"; Assert.Equal(expected, new SqlJoin <Passport>(JoinType.Inner, SqlFilter <Passport> .From(p => p.PersonId, alias).EqualTo <Person>(p => p.Id) .And(p => p.Number).IsNotNull(), alias).ToString()); }
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); }
protected virtual bool IsSimpleCrossJoinList(SqlExpression node) { SqlJoin join = (node as SqlJoin); if (join != null) { return(join.JoinType == SqlJoinType.Cross && IsSimpleCrossJoinList(join.Left) && IsSimpleCrossJoinList(join.Right)); } SqlAlias alias = (node as SqlAlias); return(alias != null && alias.Expression is SqlTable); }
internal override SqlExpression VisitScalarSubSelect(SqlSubSelect ss) { SqlSelect innerSelect = this.VisitSelect(ss.Select); if (!this.aggregateChecker.HasAggregates(innerSelect)) { innerSelect.Top = this.sql.ValueFromObject(1, ss.SourceExpression); } innerSelect.OrderingType = SqlOrderingType.Blocked; SqlAlias alias = new SqlAlias(innerSelect); this.currentSelect.From = new SqlJoin(SqlJoinType.OuterApply, this.currentSelect.From, alias, null, ss.SourceExpression); return(new SqlColumnRef(innerSelect.Row.Columns[0])); }
private SqlUnion GetUnion(SqlSource source) { SqlAlias alias = source as SqlAlias; if (alias != null) { SqlUnion union = alias.Node as SqlUnion; if (union != null) { return(union); } } return(null); }
internal override SqlAlias VisitAlias(SqlAlias a) { SqlTable tab = a.Node as SqlTable; SqlTableValuedFunctionCall tvf = a.Node as SqlTableValuedFunctionCall; if (this.addPrimaryKeys && (tab != null || tvf != null)) { List <SqlOrderExpression> list = new List <SqlOrderExpression>(); bool isTable = tab != null; MetaType rowType = isTable ? tab.RowType : tvf.RowType; foreach (MetaDataMember mm in rowType.IdentityMembers) { string name = mm.MappedName; SqlColumn col; Expression sourceExpression; List <SqlColumn> columns; if (isTable) { col = tab.Find(name); sourceExpression = tab.SourceExpression; columns = tab.Columns; } else { col = tvf.Find(name); sourceExpression = tvf.SourceExpression; columns = tvf.Columns; } if (col == null) { col = new SqlColumn(mm.MemberAccessor.Type, typeProvider.From(mm.MemberAccessor.Type), name, mm, null, sourceExpression); col.Alias = a; columns.Add(col); } list.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col))); } this.PrependOrderExpressions(list); return(a); } else { return(base.VisitAlias(a)); } }
internal override SqlSource VisitSource(SqlSource node) { node = (SqlSource)this.Visit(node); SqlAlias alias = node as SqlAlias; if (alias != null) { SqlSelect sel = alias.Node as SqlSelect; if (sel != null && this.IsTrivialSelect(sel)) { _removedMap[alias] = alias; node = sel.From; } } return(node); }
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)); }
public void GreaterThanOrEqual() { Assert.Equal("pe.Id >= 5", SqlFilter <Person> .From(m => m.Id).GreaterThanOrEqual(5).RawSql); Assert.Equal("pe.Name >= pe.LastName", SqlFilter <Person> .From(m => m.Name).GreaterThanOrEqual(m => m.LastName).RawSql); Assert.Equal("pe.Id >= pa.PersonId", SqlFilter <Person> .From(m => m.Id).GreaterThanOrEqual <Passport>(m => m.PersonId).RawSql); var peAlias = new SqlAlias <Person>("per"); var paAlias = new SqlAlias <Passport>("pas"); Assert.Equal("pe.Name >= per.LastName", SqlFilter <Person> .From(m => m.Name).GreaterThanOrEqual(m => m.LastName, peAlias).RawSql); Assert.Equal("pe.Id >= pas.PersonId", SqlFilter <Person> .From(m => m.Id).GreaterThanOrEqual <Passport>(m => m.PersonId, paAlias).RawSql); Assert.Equal("pe.Id >= COUNT(pa.Id)", SqlFilter <Person> .From(p => p.Id).GreaterThanOrEqual(SqlField <Passport> .Count(p => p.Id)).RawSql); var alias = new SqlAlias <Passport>("pasp"); Assert.Equal("pe.Id >= COUNT(pasp.Id)", SqlFilter <Person> .From(p => p.Id).GreaterThanOrEqual(SqlField <Passport> .Count(alias, p => p.Id)).ToString()); }
internal SqlAlias VisitAliasConsumed(SqlAlias a) { if (a == null) return a; bool match = false; foreach (SqlAlias alias in aliases) if (alias == a) { match = true; break; } if (match) { this.referencesAnyMatchingAliases = true; } return a; }
public void NotEqualTo() { Assert.Equal("pe.Id <> 5", SqlFilter <Person> .From(m => m.Id).NotEqualTo(5).RawSql); Assert.Equal("pe.Name <> pe.LastName", SqlFilter <Person> .From(m => m.Name).NotEqualTo(m => m.LastName).RawSql); Assert.Equal("pe.Id <> pa.PersonId", SqlFilter <Person> .From(m => m.Id).NotEqualTo <Passport>(m => m.PersonId).RawSql); var peAlias = new SqlAlias <Person>("per"); var paAlias = new SqlAlias <Passport>("pas"); Assert.Equal("pe.Name <> per.LastName", SqlFilter <Person> .From(m => m.Name).NotEqualTo(m => m.LastName, peAlias).RawSql); Assert.Equal("pe.Id <> pas.PersonId", SqlFilter <Person> .From(m => m.Id).NotEqualTo <Passport>(m => m.PersonId, paAlias).RawSql); Assert.Equal("pe.Id <> COUNT(pa.Id)", SqlFilter <Person> .From(p => p.Id).NotEqualTo(SqlField <Passport> .Count(p => p.Id)).RawSql); var alias = new SqlAlias <Passport>("pasp"); Assert.Equal("pe.Id <> COUNT(pasp.Id)", SqlFilter <Person> .From(p => p.Id).NotEqualTo(SqlField <Passport> .Count(alias, p => p.Id)).ToString()); }
internal override SqlExpression VisitColumnRef(SqlColumnRef cref) { string str = null; SqlColumn key = cref.Column; SqlAlias alias = key.Alias; if (alias == null) { this.aliasMap.TryGetValue(key, out alias); } if (alias != null && !disableAlias) { if (alias.Name == null) { if (!this.names.TryGetValue(alias, out str)) { str = "A" + this.names.Count; this.names[key.Alias] = str; } } else { str = key.Alias.Name; } } if ((!this.suppressedAliases.Contains(key.Alias) && (str != null)) && (str.Length != 0)) { this.WriteName(str); this.sb.Append("."); } string name = key.Name; string str3 = this.InferName(key.Expression, null); if (name == null) { name = str3; } if ((name == null) && !this.names.TryGetValue(key, out name)) { name = "C" + this.names.Count; this.names[key] = name; } this.WriteName(name); return(cref); }
internal override SqlAlias VisitAlias(SqlAlias alias) { bool flag = alias.Node is SqlSelect; int sourceDepth = this.depth; string str; string name = ""; SqlTable node = alias.Node as SqlTable; if (node != null) { name = node.Name; } if (alias.Name == null) { if (!this.names.TryGetValue(alias, out str)) { str = "A" + this.names.Count; this.names[alias] = str; } } else { str = alias.Name; } if (flag) { this.depth++; this.sb.Append("("); this.NewLine(); } this.Visit(alias.Node); if (flag) { this.NewLine(); this.sb.Append(")"); this.depth = sourceDepth; } if ((!this.suppressedAliases.Contains(alias) && (str != null)) && (name != str) && !disableAlias) { this.sb.Append(" AS "); this.WriteName(str); } return(alias); }
private void GetSelectionsBeforeJoin(SqlSource source, List <List <SqlColumn> > selections) { SqlJoin join = source as SqlJoin; if (join != null) { return; } SqlAlias alias = source as SqlAlias; if (alias != null) { SqlSelect select = alias.Node as SqlSelect; if (select != null) { this.GetSelectionsBeforeJoin(select.From, selections); selections.Add(select.Row.Columns); } } }
private bool IsSingletonSelect(SqlSource source) { SqlAlias alias = source as SqlAlias; if (alias == null) { return(false); } SqlSelect select = alias.Node as SqlSelect; if (select == null) { return(false); } if (select.From != null) { return(false); } return(true); }
internal SqlJoin MakeJoin(SqlJoinType joinType, SqlSource location, SqlAlias alias, SqlExpression condition, Expression source) { // if the new item is on the right side of some outer join then fixup the projection to reflect that it can possibly be null if (joinType == SqlJoinType.LeftOuter) { SqlSelect sel = alias.Node as SqlSelect; if (sel != null && sel.Selection != null && sel.Selection.NodeType != SqlNodeType.OptionalValue) { // replace selection w/ optional + outer-joined-value sel.Selection = new SqlOptionalValue( new SqlColumn( "test", this.Unary(SqlNodeType.OuterJoinedValue, this.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, source)) ), sel.Selection ); } } return(new SqlJoin(joinType, location, alias, condition, source)); }
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; }
protected override SqlExpression VisitAlias(SqlAlias alias) { bool isSelect = (alias.Expression is SqlSelect); int depth = _depth; string name = String.Empty; string aliasName = null; SqlField field = (alias.Expression as SqlField); if (field != null) name = field.Name; SqlTable table = (alias.Expression as SqlTable); if (table != null) name = table.Name; if (alias.Name == null) { if (!_nameMap.TryGetValue(alias, out aliasName)) { aliasName = "A" + _nameMap.Count; _nameMap[alias] = aliasName; } } else aliasName = alias.Name; if (isSelect) { _depth++; _builder.Append("("); this.NewLine(); } Visit(alias.Expression); if (isSelect) { this.NewLine(); _builder.Append(")"); _depth = depth; } if ((!_suppressedAliases.Contains(alias) && aliasName != null) && name != aliasName) { _builder.Append(" AS "); WriteName(aliasName); } return alias; }
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 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); }
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 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; }
/// <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; }
internal override SqlAlias VisitAlias(SqlAlias a) { SqlTable tab = a.Node as SqlTable; SqlTableValuedFunctionCall tvf = a.Node as SqlTableValuedFunctionCall; if (this.addPrimaryKeys && (tab != null || tvf != null)) { List<SqlOrderExpression> list = new List<SqlOrderExpression>(); bool isTable = tab != null; MetaType rowType = isTable ? tab.RowType : tvf.RowType; foreach (MetaDataMember mm in rowType.IdentityMembers) { string name = mm.MappedName; SqlColumn col; Expression sourceExpression; List<SqlColumn> columns; if (isTable) { col = tab.Find(name); sourceExpression = tab.SourceExpression; columns = tab.Columns; } else { col = tvf.Find(name); sourceExpression = tvf.SourceExpression; columns = tvf.Columns; } if (col == null) { col = new SqlColumn(mm.MemberAccessor.Type, typeProvider.From(mm.MemberAccessor.Type), name, mm, null, sourceExpression); col.Alias = a; columns.Add(col); } list.Add(new SqlOrderExpression(SqlOrderType.Ascending, new SqlColumnRef(col))); } this.PrependOrderExpressions(list); return a; } else { return base.VisitAlias(a); } }
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 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; } }
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; }
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); } }
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); }
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); } }
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 override SqlAlias VisitAlias(SqlAlias alias) { bool isSelect = alias.Node is SqlSelect; int saveDepth = this.depth; string aliasName = null; string name = ""; SqlTable table = alias.Node as SqlTable; if (table != null) { name = table.Name; } if (alias.Name == null) { if (!this.names.TryGetValue(alias, out aliasName)) { aliasName = "A" + this.names.Count; this.names[alias] = aliasName; } } else { aliasName = alias.Name; } if (isSelect) { this.depth++; sb.Append("("); this.NewLine(); } this.Visit(alias.Node); if (isSelect) { this.NewLine(); sb.Append(")"); this.depth = saveDepth; } if (!this.suppressedAliases.Contains(alias) && aliasName != null && name != aliasName) { sb.Append(" AS "); this.WriteName(aliasName); } return 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 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 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(); } } }
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); }
internal override SqlAlias VisitAlias(SqlAlias a) { SqlAlias save = this.currentAlias; this.currentAlias = a; base.VisitAlias(a); this.currentAlias = save; return a; }
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; }
private void WriteAliasName(SqlAlias alias) { string aliasName = null; if (alias.Name == null) { if (!this.names.TryGetValue(alias, out aliasName)) { aliasName = "A" + this.names.Count; this.names[alias] = aliasName; } } else { aliasName = alias.Name; } this.WriteName(aliasName); }
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; } }