protected virtual Expression AnalyzeProjectionQuery(SqlFunctionType specialExpressionType, IList<Expression> parameters, TranslationContext context, bool canHaveFilter = true) { if (context.IsExternalInExpressionChain) { var operand0 = Analyze(parameters[0], context); Expression functionOperand = null; Expression projectionOperand; if ( context.CurrentSelect.NextSelectExpression != null || context.CurrentSelect.Operands.Count() > 0 || context.CurrentSelect.Group.Count > 0 ) { // No TableInfo in projection operand0 = new SubSelectExpression(context.CurrentSelect, operand0.Type, "source", null); context.NewParentSelect(); // In the new scope we should not have MaximumDatabaseLoad //context.QueryContext.MaximumDatabaseLoad = false; context.CurrentSelect.Tables.Add(operand0 as TableExpression); } // basically, we have three options for projection methods: // - projection on grouped table (1 operand, a GroupExpression) // - projection on grouped column (2 operands, GroupExpression and ColumnExpression) // - projection on table/column, with optional restriction var groupOperand0 = operand0 as GroupExpression; if (groupOperand0 != null) { if (parameters.Count > 1) projectionOperand = Analyze(parameters[1], groupOperand0.GroupedExpression, context); else projectionOperand = Analyze(groupOperand0.GroupedExpression, context); } else { projectionOperand = operand0; if (parameters.Count > 1) functionOperand = Analyze(parameters[1], operand0, context); int filterIndex = canHaveFilter ? 1 : -1; //special case for Average - its second parameter is NOT filter CheckWhere(projectionOperand, parameters, filterIndex, context); } if (projectionOperand is TableExpression) projectionOperand = RegisterTable((TableExpression)projectionOperand, context); if (groupOperand0 != null) { var childColumns = GetChildColumns(projectionOperand, context); projectionOperand = new GroupExpression(projectionOperand, groupOperand0.KeyExpression, childColumns); } var opList = new List<Expression>(); opList.Add(projectionOperand); if (functionOperand != null) opList.Add(functionOperand); return CreateSqlFunction(specialExpressionType, opList.ToArray()); } else { var subQueryContext = context.NewSelect(); var tableExpression = Analyze(parameters[0], subQueryContext); //RI: new stuff - handling grouping with aggregates //if (IsAggregate(specialExpressionType)) { var grpExpr = tableExpression as GroupExpression; var srcTable = grpExpr == null ? tableExpression : grpExpr.GroupedExpression ; SqlFunctionExpression specialExpr; if (parameters.Count > 1) { var predicate = Analyze(parameters[1], srcTable, subQueryContext); specialExpr = CreateSqlFunction(specialExpressionType, tableExpression, predicate); } else { specialExpr = CreateSqlFunction(specialExpressionType, tableExpression); } // If subQuery context has no tables added, it is not a subquery, it's just an aggregate function over 'main' table var currSelect = subQueryContext.CurrentSelect; if (currSelect.Tables.Count == 0) return specialExpr; //this is a real subquery, so mutate and return the current select from this context currSelect = currSelect.ChangeOperands(new Expression[] { specialExpr }, currSelect.Operands); return currSelect; //} //RI: end my special code } }
/// <summary> /// Any() returns true if the given condition satisfies at least one of provided elements /// </summary> /// <param name="parameters"></param> /// <param name="context"></param> /// <returns></returns> protected virtual Expression AnalyzeAny(IList<Expression> parameters, TranslationContext context) { if (context.IsExternalInExpressionChain) { var tableExpression = Analyze(parameters[0], context); Expression projectionOperand; if (context.CurrentSelect.NextSelectExpression != null) { TableExpression currentTableExpression = tableExpression as TableExpression; tableExpression = new SubSelectExpression(context.CurrentSelect, currentTableExpression.Type, "source", currentTableExpression.TableInfo); context.NewParentSelect(); // In the new scope we should not have MaximumDatabaseLoad //context.QueryContext.MaximumDatabaseLoad = false; context.CurrentSelect.Tables.Add(tableExpression as TableExpression); } // basically, we have three options for projection methods: // - projection on grouped table (1 operand, a GroupExpression) // - projection on grouped column (2 operands, GroupExpression and ColumnExpression) // - projection on table/column, with optional restriction var groupOperand0 = tableExpression as GroupExpression; if (groupOperand0 != null) { if (parameters.Count > 1) { projectionOperand = Analyze(parameters[1], groupOperand0.GroupedExpression, context); } else projectionOperand = Analyze(groupOperand0.GroupedExpression, context); } else { projectionOperand = tableExpression; CheckWhere(projectionOperand, parameters, 1, context); } if (projectionOperand is TableExpression) projectionOperand = RegisterTable((TableExpression)projectionOperand, context); if (groupOperand0 != null) { var childColumns = GetChildColumns(groupOperand0.KeyExpression, context); projectionOperand = new GroupExpression(projectionOperand, groupOperand0.KeyExpression, childColumns); } return ExpressionUtil.MakeGreaterThanZero(CreateSqlFunction(SqlFunctionType.Count, projectionOperand)); } else { var anyBuilderContext = context.NewSelect(); var tableExpression = Analyze(parameters[0], anyBuilderContext); if (!(tableExpression is TableExpression)) tableExpression = Analyze(tableExpression, anyBuilderContext); // from here we build a custom clause: // <anyClause> ==> "(select count(*) from <table> where <anyClause>)>0" // TODO (later...): see if some vendors support native Any operator and avoid this substitution if (parameters.Count > 1) { var anyClause = Analyze(parameters[1], tableExpression, anyBuilderContext); RegisterWhere(anyClause, anyBuilderContext); } var countExpr = CreateSqlFunction(SqlFunctionType.Count, tableExpression); var currSelect = anyBuilderContext.CurrentSelect; anyBuilderContext.CurrentSelect = currSelect.ChangeOperands(new Expression[] {countExpr}, currSelect.Operands); // TODO: see if we need to register the tablePiece here (we probably don't) // we now switch back to current context, and compare the result with 0 // note - we might have count returning int or long, so we must adjust 0 constant (int or long) var anyExpression = ExpressionUtil.MakeGreaterThanZero(anyBuilderContext.CurrentSelect); return anyExpression; } }
protected virtual Expression AnalyzeContains(IList<Expression> parameters, TranslationContext context) { if (!parameters[1].Type.IsDbPrimitive()) parameters = ConvertContainsWithObject(parameters, context); var t0 = parameters[0].Type; if (t0.IsGenericQueryable()) { Expression p1 = Analyze(parameters[1], context); var newContext = context.NewSelect(); Expression p0 = AnalyzeSubQuery(parameters[0], newContext); var c = p0 as ColumnExpression; if (c != null && !newContext.CurrentSelect.Tables.Contains(c.Table)) newContext.CurrentSelect.Tables.Add(c.Table); return CreateSqlFunction(SqlFunctionType.In, p1, newContext.CurrentSelect.Mutate(new Expression[] { p0 })); } else if (t0.IsListOrArray()) { Expression array = Analyze(parameters[0], context); var expression = Analyze(parameters[1], context); return CreateSqlFunction(SqlFunctionType.InArray, expression, array); } throw Util.Throw("{0}.Contains() method is not supported.", t0.GetDisplayName()); }
protected virtual Expression AnalyzeAll(IList<Expression> parameters, TranslationContext context) { var allBuilderContext = context.NewSelect(); var tableExpression = Analyze(parameters[0], allBuilderContext); var allClause = Analyze(parameters[1], tableExpression, allBuilderContext); // from here we build a custom clause: // <allClause> ==> "(select count(*) from <table> where not <allClause>)==0" // TODO (later...): see if some vendors support native All operator and avoid this substitution var whereExpression = Expression.Not(allClause); RegisterWhere(whereExpression, allBuilderContext); var countExpr = CreateSqlFunction(SqlFunctionType.Count, tableExpression); var currSelect = allBuilderContext.CurrentSelect; allBuilderContext.CurrentSelect = currSelect.ChangeOperands(new Expression[] {countExpr}, currSelect.Operands); // TODO: see if we need to register the tablePiece here (we probably don't) // we now switch back to current context, and compare the result with 0 // Note: be careful, result of Count() might be int32 or int64 for different servers, so user CreateConstant helper var zero = ExpressionUtil.CreateConstant(0, allBuilderContext.CurrentSelect.Type); var allExpression = Expression.Equal(allBuilderContext.CurrentSelect, zero); return allExpression; }