private static QueryBuffer.TypeBuffer GetTypeBuffer(HarmonyTableExpression table, List <Tuple <HarmonyTableExpression, QueryBuffer.TypeBuffer> > flatList)
        {
            var queryTables = table.RootExpression.RootExpressions.Values.OfType <HarmonyTableExpression>().Where(qt => qt != table);
            var made        = new QueryBuffer.TypeBuffer
            {
                DataObjectType  = table.ItemType,
                IsCollection    = table.IsCollection,
                ParentFieldName = table.Name,
                Metadata        = DataObjectMetadataBase.LookupType(table.ItemType)
            };

            if (table.Top != null)
            {
                var bakedFunction = Expression.Lambda <Func <QueryContext, long> >(Expression.Convert(table.Top, typeof(long)), QueryCompilationContext.QueryContextParameter).Compile();
                made.Top = (obj) => bakedFunction(obj as QueryContext);
            }

            if (table.Skip != null)
            {
                var bakedFunction = Expression.Lambda <Func <QueryContext, long> >(Expression.Convert(table.Skip, typeof(long)), QueryCompilationContext.QueryContextParameter).Compile();
                made.Skip = (obj) => bakedFunction(obj as QueryContext);
            }

            if (made.Top != null || made.Skip != null)
            {
                made.SelectResult = CollectionFilterMethod.MakeGenericMethod(made.DataObjectType).CreateDelegate(typeof(Func <QueryBuffer, QueryBuffer.TypeBuffer, object, object>), null) as Func <QueryBuffer, QueryBuffer.TypeBuffer, object, object>;
            }

            flatList.Add(Tuple.Create(table, made));
            var joinedBuffers = queryTables.OfType <HarmonyTableExpression>().Where(qt => !qt.Name.Contains(".")).Select(qt => GetTypeBuffer(qt, flatList)).ToList();
            var namedLookup   = joinedBuffers.ToDictionary(tb => tb.ParentFieldName);

            foreach (var nestedTable in queryTables.OfType <HarmonyTableExpression>().Where(qt => qt.Name.Contains(".")).OrderBy(qt => qt.Name.Length))
            {
                var nameParts = nestedTable.Name.Split(".");
                if (namedLookup.TryGetValue(string.Join(".", nameParts.Take(nameParts.Length - 1)), out var foundBuffer))
                {
                    var fullName = nestedTable.Name;
                    nestedTable.Name = nameParts.Last();
                    var madeBuffer = GetTypeBuffer(nestedTable, flatList);
                    foundBuffer.JoinedBuffers.Add(madeBuffer);
                    namedLookup.Add(fullName, madeBuffer);
                }
                else
                {
                    throw new Exception(string.Format("failed to find parent table while processing query {0}", nestedTable.Name));
                }
            }
            made.JoinedBuffers = joinedBuffers;
            return(made);
        }
        public static object CollectionFilter <T>(QueryBuffer query, QueryBuffer.TypeBuffer typeBuf, object result)
        {
            var typedResult = result as IEnumerable <T>;

            if (typedResult != null)
            {
                if (typeBuf.Skip != null)
                {
                    typedResult = typedResult.Skip((int)typeBuf.Skip(query.Context));
                }

                if (typeBuf.Top != null)
                {
                    typedResult = typedResult.Take((int)typeBuf.Top(query.Context));
                }
                return(typedResult.ToList());
            }
            else
            {
                return(result);
            }
        }
        public PreparedQueryPlan PrepareQuery(HarmonyQueryCompilationContext compilationContext)
        {
            var rootExpr               = RootExpressions[_valueBufferParameter];
            var processedOns           = new List <object>();
            var flatList               = new List <Tuple <HarmonyTableExpression, QueryBuffer.TypeBuffer> >();
            var typeBuffers            = new QueryBuffer.TypeBuffer[] { GetTypeBuffer(rootExpr, flatList) };
            var expressionTableMapping = flatList.ToDictionary(kvp => kvp.Item1.RootExpression.ConvertedParameter as Expression, kvp => kvp.Item1 as IHarmonyQueryTable, new ExpressionValueComparer());
            var tableList              = flatList.Select(tpl => tpl.Item1 as IHarmonyQueryTable).ToList();

            //extract all of expressions that might represent a given table and add them to the mapping dictionary
            foreach (var table in tableList)
            {
                foreach (var alias in ((HarmonyTableExpression)table).Aliases)
                {
                    if (!expressionTableMapping.ContainsKey(alias))
                    {
                        expressionTableMapping.Add(alias, table);
                    }
                }
            }

            var whereBuilder = new WhereExpressionBuilder(rootExpr.IsCaseSensitive, tableList, expressionTableMapping);

            var processedWheres = new List <Object>();
            var orderBys        = new List <Tuple <FileIO.Queryable.FieldReference, bool> >();

            foreach (var expr in rootExpr.WhereExpressions)
            {
                whereBuilder.VisitForWhere(expr, processedWheres, processedOns);
            }

            foreach (var tpl in flatList)
            {
                foreach (var expr in tpl.Item1.OnExpressions)
                {
                    var madeOn = whereBuilder.VisitForOn(expr);
                    if (madeOn != null)
                    {
                        processedOns.Add(madeOn);
                        if (tpl.Item2.JoinOn == null)
                        {
                            tpl.Item2.JoinOn = madeOn;
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }
                }

                if (tpl.Item1 != rootExpr)
                {
                    foreach (var expr in tpl.Item1.WhereExpressions)
                    {
                        var madeOn = whereBuilder.VisitForOn(expr);
                        if (madeOn != null)
                        {
                            processedOns.Add(madeOn);
                            if (tpl.Item2.JoinOn != null)
                            {
                                madeOn = new ConnectorPart()
                                {
                                    Op = WhereClauseConnector.AndOperator, Left = tpl.Item2.JoinOn, Right = madeOn
                                }
                            }
                            ;

                            tpl.Item2.JoinOn = madeOn;
                        }
                    }
                }

                foreach (var expr in tpl.Item1.OrderByExpressions)
                {
                    var fieldRef = whereBuilder.VisitForOrderBy(expr.Item1);
                    if (fieldRef != null)
                    {
                        orderBys.Add(Tuple.Create(fieldRef, expr.Item2));
                    }
                }
            }

            var queryBuffer = new QueryBuffer(flatList.Select(tpl => tpl.Item2).ToList());

            var fieldReferences = new Dictionary <int, List <FieldDataDefinition> >();

            foreach (var queryExpr in flatList)
            {
                if (queryExpr.Item1.ReferencedFields.Count > 0)
                {
                    var bufferIndex = queryBuffer.TypeBuffers.IndexOf(queryExpr.Item2);
                    fieldReferences.Add(bufferIndex, queryExpr.Item1.ReferencedFields);
                }
            }

            var queryPlan = new PreparedQueryPlan(true, processedWheres, fieldReferences, processedOns,
                                                  orderBys, queryBuffer, "");

            return(queryPlan);
        }