protected override ScalarExpression OnBuildQuery(QueryBuilderContext context) { // Currently this must be a literal var field = Right as ConstantEntityNode; if (field == null) { throw new ParseException("Field required."); } var queryNode = context.GetNode(Left); // Start workaround for #27148 // If an aggregate operation is performed on a choice field, then we try to treat it like an entity // then we have a scenario that can't currently be represented in structured queries. // (Because sq aggregate operations get declared as expressions, but if it's an expression we can no longer use it as a sq node for other expressions). // This can happen if we use the choice-field in a comparison (e.g. max(choice)>whatever) because the compiler // will inject a field lookup to redirect to the ordering field. // For now, detect this specific scenario, and pass the aggregate expression directly through because the query engine // will defer to the ordering version of the SQL, which will in turn look up the enumOrder field. // However, ideally we should also be able to do things like max(choice).someOtherRelationshipOrField bool nodeIsAggregate = Left is AggregateNode || Left is EntityTypeCast && Left.Arguments[0] is AggregateNode; bool fieldIsEnumOrdering = field.Instance.Id == WellKnownAliases.CurrentTenant.EnumOrder; if (nodeIsAggregate && fieldIsEnumOrdering) { return(Left.BuildQuery(context)); } // End workaround var result = new ResourceDataColumn { FieldId = field.Instance, NodeId = queryNode.NodeId }; return(result); }
protected override ScalarExpression OnBuildQuery(QueryBuilderContext context) { ScalarExpression arg = Argument.BuildQuery(context); if (ResultType.Type == DataType.Entity) { return(arg); } // Special case for AggregateExpression, because if we aggregate a choicefield, then it presents // as an Entity type, therefore requesting this cast - but the query builder wants to receive the aggregate expression directly. if (arg is EDC.ReadiNow.Metadata.Query.Structured.AggregateExpression) { var result = new MutateExpression { Expression = arg }; switch (ResultType.Type) { case DataType.String: result.MutateType = MutateType.DisplaySql; break; case DataType.Bool: result.MutateType = MutateType.BoolSql; break; default: throw new InvalidOperationException(ResultType.Type.ToString( )); } return(result); } // Just refer to the node var queryNode = context.GetNode(Argument); switch (ResultType.Type) { case DataType.String: var nameResult = new ResourceDataColumn { FieldId = "core:name", NodeId = queryNode.NodeId }; return(nameResult); case DataType.Bool: var boolResult = new CalculationExpression { Operator = CalculationOperator.IsNull, Expressions = new List <ScalarExpression> { new IdExpression { NodeId = queryNode.NodeId } } }; return(boolResult); default: throw new InvalidOperationException(ResultType.Type.ToString()); } }