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