/// <summary> /// Builds a query plan. /// </summary> /// <param name="messages"> /// The messages. /// </param> /// <param name="data"> /// The data. /// </param> /// <param name="node"> /// The script. /// </param> /// <returns> /// The <see cref="Task"/>. /// </returns> public static IQueryPlan Build(IMessageWriter messages, INodeDataProvider data, Node node) { var generator = new QueryPlanBuilder(messages, data); generator.Visit(node); return(data.GetQueryPlan(node)); }
protected internal override Node VisitSelectSource(SelectSource node) { node = (SelectSource)base.VisitSelectSource(node); var selectQuery = QueryPlanBuilder.Build(this.messages, this.data, node.Select); var aliases = new HashSet <string>( new[] { node.Alias, }); var factory = Expression.New( typeof(SelectDataSource).GetTypeInfo().DeclaredConstructors.First(), Expression.Constant(selectQuery), Expression.Constant(aliases)); this.data.SetFactoryExpression(node, factory); return(node); }
/// <summary> /// Gets a function that creates the values for the selected records from a row. /// </summary> /// <param name="expressions"> /// The <see cref="AliasedSqlExpression"/>s to get the values for. /// </param> /// <param name="fieldList"> /// A collection that will be filled with all the fields that are used in the select statement. /// </param> /// <param name="wildCardAliasList"> /// A collection that will be filled with all the wildcards that are used in the select statement. /// </param> /// <param name="allSourceAliases"> /// Contains all source aliases in the query, so when a global wildcard is found, all fields are added. /// </param> /// <returns> /// A delegate. /// </returns> private Delegate GetValueFactory([NotNull] IEnumerable <AliasedSqlExpression> expressions, ICollection <IField> fieldList, ICollection <string> wildCardAliasList, IEnumerable <string> allSourceAliases) { var row = Expression.Parameter(typeof(Row), "row"); var context = Expression.Parameter(typeof(IExecutionContext), "context"); var fieldFactories = new List <LambdaExpression>(); var lambdaVisitor = new GenericVisitor { (ExecutionContextExpression e) => context, (SourceFieldExpression e) => { fieldList?.Add(new Field(e.SourceName, e.FieldName)); return(e.CreateGetter(row)); }, (UnaryExpression e) => e.NodeType == ExpressionType.Convert ? (e.Operand as SourceFieldExpression)?.CreateGetter(row, e.Type) : null, }; Func <string, Expression, Expression <Func <IExecutionContext, Row, KeyValuePair <string, object> > > > convertToLambda = (alias, expression) => { var lambda = lambdaVisitor.Visit(expression.EvaluateAsValue()); lambda = lambda.Type == typeof(object) ? lambda : Expression.Convert(lambda, typeof(object)); return(Expression.Lambda <Func <IExecutionContext, Row, KeyValuePair <string, object> > >(Expression.New(QueryPlanBuilder.KeyValuePairConstructor, Expression.Constant(alias), lambda.CatchErrors()), context, row)); }; Expression <ValueFactory> result = null; var wildcardIndex = 0; var hasGlobalAlias = false; foreach (var expression in expressions) { if (expression.Expression is WildcardSqlExpression wildcard) { if (wildcard.Source != null) { wildCardAliasList.Add(wildcard.Source); } else { hasGlobalAlias = true; } var sourcePrefix = $"{wildcard.Source}."; var index = wildcardIndex++; var lambda = string.IsNullOrEmpty(wildcard.Source) ? (Expression <ValueFactory>)((c, r) => r.ColumnNames.Select(cn => new KeyValuePair <string, object>($"{index}!{cn}", r[cn]))) : (c, r) => r.ColumnNames.Where(cn => cn.StartsWith(sourcePrefix)).Select(cn => new KeyValuePair <string, object>($"{index}!{cn}", r[cn])); lambda = lambda.ReplaceParameter(lambda.Parameters[0], context).ReplaceParameter(lambda.Parameters[1], row); if (fieldFactories.Count != 0) { var fields = QueryPlanBuilder.ToArrayInit <ValueFactory>(fieldFactories); result = result == null ? fields : QueryPlanBuilder.ConcatenateLambdas(result, fields); fieldFactories.Clear(); } result = result == null ? lambda : QueryPlanBuilder.ConcatenateLambdas(result, lambda); } else { fieldFactories.Add(convertToLambda(expression.Alias, this.data.ConvertToLinqExpression(expression.Expression))); } } if (fieldFactories.Count != 0) { var fields = QueryPlanBuilder.ToArrayInit <ValueFactory>(fieldFactories); result = result == null ? fields : QueryPlanBuilder.ConcatenateLambdas(result, fields); } if (hasGlobalAlias) { foreach (var alias in allSourceAliases) { wildCardAliasList.Add(alias); } } var asyncResult = (result ?? ((c, r) => Enumerable.Empty <KeyValuePair <string, object> >())).RewriteTasksToAsyncExpression(); return(asyncResult.ReturnType == typeof(IEnumerable <KeyValuePair <string, object> >) ? ((Expression <ValueFactory>)asyncResult).Compile() : (Delegate)((Expression <AsyncValueFactory>)asyncResult).Compile()); }