示例#1
0
        public override IDataExecutionPlanNode FoldQuery(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints)
        {
            var leftSchema = LeftSource.GetSchema(dataSources, parameterTypes);

            LeftSource        = LeftSource.FoldQuery(dataSources, options, parameterTypes, hints);
            LeftSource.Parent = this;

            var innerParameterTypes = GetInnerParameterTypes(leftSchema, parameterTypes);

            RightSource        = RightSource.FoldQuery(dataSources, options, innerParameterTypes, hints);
            RightSource.Parent = this;
            return(this);
        }
示例#2
0
        public override IDataExecutionPlanNode FoldQuery(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints)
        {
            LeftSource         = LeftSource.FoldQuery(dataSources, options, parameterTypes, hints);
            LeftSource.Parent  = this;
            RightSource        = RightSource.FoldQuery(dataSources, options, parameterTypes, hints);
            RightSource.Parent = this;

            if (SemiJoin)
            {
                return(this);
            }

            var leftSchema  = LeftSource.GetSchema(dataSources, parameterTypes);
            var rightSchema = RightSource.GetSchema(dataSources, parameterTypes);

            if (LeftSource is FetchXmlScan leftFetch && RightSource is FetchXmlScan rightFetch)
            {
                // Can't join data from different sources
                if (!leftFetch.DataSource.Equals(rightFetch.DataSource, StringComparison.OrdinalIgnoreCase))
                {
                    return(this);
                }

                // If one source is distinct and the other isn't, joining the two won't produce the expected results
                if (leftFetch.FetchXml.distinct ^ rightFetch.FetchXml.distinct)
                {
                    return(this);
                }

                // Check that the alias is valid for FetchXML
                if (!FetchXmlScan.IsValidAlias(rightFetch.Alias))
                {
                    return(this);
                }

                var leftEntity  = leftFetch.Entity;
                var rightEntity = rightFetch.Entity;

                // Check that the join is on columns that are available in the FetchXML
                var leftAttribute = LeftAttribute.GetColumnName();
                if (!leftSchema.ContainsColumn(leftAttribute, out leftAttribute))
                {
                    return(this);
                }
                var rightAttribute = RightAttribute.GetColumnName();
                if (!rightSchema.ContainsColumn(rightAttribute, out rightAttribute))
                {
                    return(this);
                }
                var leftAttributeParts  = leftAttribute.Split('.');
                var rightAttributeParts = rightAttribute.Split('.');
                if (leftAttributeParts.Length != 2)
                {
                    return(this);
                }
                if (rightAttributeParts.Length != 2)
                {
                    return(this);
                }

                // If the entities are from different virtual entity data providers it's probably not going to work
                if (!dataSources.TryGetValue(leftFetch.DataSource, out var dataSource))
                {
                    throw new NotSupportedQueryFragmentException("Missing datasource " + leftFetch.DataSource);
                }

                if (dataSource.Metadata[leftFetch.Entity.name].DataProviderId != dataSource.Metadata[rightFetch.Entity.name].DataProviderId)
                {
                    return(this);
                }

                // Check we're not going to have too many link entities
                var leftLinkCount  = leftFetch.Entity.GetLinkEntities().Count();
                var rightLinkCount = rightFetch.Entity.GetLinkEntities().Count() + 1;

                if (leftLinkCount + rightLinkCount > 10)
                {
                    return(this);
                }

                // If we're doing a right outer join, switch everything round to do a left outer join
                // Also switch join order for inner joins to use N:1 relationships instead of 1:N to avoid problems with paging
                if (JoinType == QualifiedJoinType.RightOuter ||
                    JoinType == QualifiedJoinType.Inner && !rightAttributeParts[0].Equals(rightFetch.Alias, StringComparison.OrdinalIgnoreCase) ||
                    JoinType == QualifiedJoinType.Inner && leftAttribute == leftSchema.PrimaryKey && rightAttribute != rightSchema.PrimaryKey)
                {
                    Swap(ref leftFetch, ref rightFetch);
                    Swap(ref leftEntity, ref rightEntity);
                    Swap(ref leftAttribute, ref rightAttribute);
                    Swap(ref leftAttributeParts, ref rightAttributeParts);
                    Swap(ref leftSchema, ref rightSchema);
                }

                // Must be joining to the root entity of the right source, i.e. not a child link-entity
                if (!rightAttributeParts[0].Equals(rightFetch.Alias, StringComparison.OrdinalIgnoreCase))
                {
                    return(this);
                }

                // If there are any additional join criteria, either they must be able to be translated to FetchXml criteria
                // in the new link entity or we must be using an inner join so we can use a post-filter node
                var additionalCriteria     = AdditionalJoinCriteria;
                var additionalLinkEntities = new Dictionary <object, List <FetchLinkEntityType> >();

                if (TranslateFetchXMLCriteria(dataSource.Metadata, options, additionalCriteria, rightSchema, rightFetch.Alias, rightEntity.name, rightFetch.Alias, rightEntity.Items, out var filter, additionalLinkEntities))
                {
                    rightEntity.AddItem(filter);

                    foreach (var kvp in additionalLinkEntities)
                    {
                        if (kvp.Key is FetchEntityType e)
                        {
                            foreach (var le in kvp.Value)
                            {
                                rightEntity.AddItem(le);
                            }
                        }
                        else
                        {
                            foreach (var le in kvp.Value)
                            {
                                ((FetchLinkEntityType)kvp.Key).AddItem(le);
                            }
                        }
                    }

                    additionalCriteria = null;
                }

                if (additionalCriteria != null && JoinType != QualifiedJoinType.Inner)
                {
                    return(this);
                }

                var rightLinkEntity = new FetchLinkEntityType
                {
                    alias    = rightFetch.Alias,
                    name     = rightEntity.name,
                    linktype = JoinType == QualifiedJoinType.Inner ? "inner" : "outer",
                    from     = rightAttributeParts[1].ToLowerInvariant(),
                    to       = leftAttributeParts[1].ToLowerInvariant(),
                    Items    = rightEntity.Items
                };

                // Find where the two FetchXml documents should be merged together and return the merged version
                if (leftAttributeParts[0].Equals(leftFetch.Alias))
                {
                    if (leftEntity.Items == null)
                    {
                        leftEntity.Items = new object[] { rightLinkEntity }
                    }
                    ;
                    else
                    {
                        leftEntity.Items = leftEntity.Items.Concat(new object[] { rightLinkEntity }).ToArray();
                    }
                }
                else
                {
                    var leftLinkEntity = leftFetch.Entity.FindLinkEntity(leftAttributeParts[0]);

                    if (leftLinkEntity == null)
                    {
                        return(this);
                    }

                    if (leftLinkEntity.Items == null)
                    {
                        leftLinkEntity.Items = new object[] { rightLinkEntity }
                    }
                    ;
                    else
                    {
                        leftLinkEntity.Items = leftLinkEntity.Items.Concat(new object[] { rightLinkEntity }).ToArray();
                    }
                }

                if (additionalCriteria != null)
                {
                    return new FilterNode {
                               Filter = additionalCriteria, Source = leftFetch
                    }
                }