private static string GetExpandedSelect(FetchLinkEntityType linkitem, string relation, FetchXmlBuilder sender) { if (linkitem.Items == null) { return(""); } var resultList = new List <string>(); var linkentity = linkitem.name; var attributeitems = linkitem.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList(); if (linkitem.intersect) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; linkentity = nextlink.name; attributeitems = nextlink.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList(); } } if (attributeitems.Count > 0) { foreach (FetchAttributeType attributeitem in attributeitems) { resultList.Add(relation + "/" + LogicalToSchemaName(linkentity, attributeitem.name, sender)); } } return(string.Join(",", resultList)); }
private static void removeUnnecessaryLinksAttributes(savedqueryLayoutxmlGrid viewFeo, ViewExcelSheetsInfo excelData) { FetchEntityType currentEt = excelData.fetchObj.Items.Where(x => x is FetchEntityType).Select(x => (FetchEntityType)x).FirstOrDefault(); ViewsRelationsObj obj = excelData.relationsList.Where(x => x.relationAlias == null).FirstOrDefault(); if (currentEt != null && obj != null) { List <object> array = currentEt.Items.ToList(); for (int i = array.Count - 1; i > -1; i--) { object currentItem = array[i]; if (currentItem is FetchLinkEntityType) { FetchLinkEntityType link = currentItem as FetchLinkEntityType; removeUnnecessaryAttributesInner(link, viewFeo); if (link == null || link.visible == false) { if (link == null || link.Items.Count() == 0) { array.RemoveAt(i); } } } else if (currentItem is FetchAttributeType) { FetchAttributeType attr = currentItem as FetchAttributeType; if (!(viewFeo.row.cell.Where(x => x.name == attr.name).Count() > 0) && attr.name != obj.entityMetadata.PrimaryIdAttribute) { array.RemoveAt(i); } } } currentEt.Items = array.ToArray(); } }
private static List <string> GetExpandedSelect(FetchLinkEntityType linkitem, string relation) { var resultList = new List <string>(); if (linkitem.Items != null) { var attributeitems = linkitem.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList(); if (linkitem.intersect) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; attributeitems = nextlink.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList(); } } if (attributeitems.Count > 0) { foreach (FetchAttributeType attributeitem in attributeitems) { resultList.Add(relation + "." + attributeitem.name); } } } return(resultList); }
private static string GetExpandedSelect(FetchLinkEntityType linkitem, FetchXmlBuilder sender, string entityname = "") { if (linkitem.Items == null) { return(null); } var linkentity = linkitem.name; if (linkentity == null) { return(null); } var attributeitems = linkitem.Items .OfType <FetchAttributeType>() .Where(i => i.name != null); if (linkitem.intersect) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; linkentity = nextlink.name; if (nextlink.Items == null) { return(null); } attributeitems = nextlink.Items .OfType <FetchAttributeType>() .Where(i => i.name != null); } } var resultList = GetAttributeNames(linkentity, attributeitems, sender); var expandedSelect = string.Join(",", resultList); var nestedExpand = GetNestedExpand(linkitem, sender, linkitem.name); var options = new List <string>(); if (!String.IsNullOrEmpty(expandedSelect)) { options.Add("$select=" + expandedSelect); } if (!String.IsNullOrEmpty(nestedExpand)) { options.Add("$expand=" + nestedExpand); } return(String.Join(";", options)); }
private static void updateViewRelatedObj(FetchType fetchType, string attributeName, List <ViewsRelationsObj> relationObj) { int index = attributeName.IndexOf("."); if (index == -1) { if (relationObj.Where(x => x.relationAlias == null).Count() == 0) { foreach (object fetch in fetchType.Items) { if (fetch is FetchEntityType) { foreach (object obj in ((FetchEntityType)fetch).Items) { if (obj is FetchAttributeType && attributeName == ((FetchAttributeType)obj).name) { FetchEntityType fetchent = (FetchEntityType)fetch; relationObj.Add(new ViewsRelationsObj() { entity = fetchent.name, entityMetadata = GlobalOperations.Instance.CRMOpHelper.RetriveEntityAtrribute(fetchent.name) }); } } } } } } else { string alias = attributeName.Substring(0, index); if (relationObj.Where(x => x.relationAlias != null && attributeName.StartsWith(x.relationAlias)).Count() == 0) { foreach (object fetch in fetchType.Items) { foreach (object obj in ((FetchEntityType)fetch).Items) { if (obj is FetchLinkEntityType) { FetchLinkEntityType relation = getFechXmlFromLinks((FetchLinkEntityType)obj, attributeName); if (relation != null) { relationObj.Add(new ViewsRelationsObj() { entity = relation.name, entityMetadata = GlobalOperations.Instance.CRMOpHelper.RetriveEntityAtrribute(relation.name), relationAlias = relation.alias, relationFrom = relation.from, relationTo = relation.to }); } } } } } } }
private void RemoveAttributesAndOrders(FetchLinkEntityType entity) { if (entity.Items != null) { entity.Items = entity.Items.Where(o => !(o is FetchAttributeType) && !(o is allattributes) && !(o is FetchOrderType)).ToArray(); foreach (var linkEntity in entity.Items.OfType<FetchLinkEntityType>()) RemoveAttributesAndOrders(linkEntity); } }
public static void AddItem(this FetchLinkEntityType linkEntity, object item) { if (linkEntity.Items == null) { linkEntity.Items = new[] { item } } ; else { linkEntity.Items = linkEntity.Items.Concat(new[] { item }).ToArray(); } }
private static string GetExpandedSelect(FetchLinkEntityType linkitem, FetchXmlBuilder sender) { if (linkitem.Items == null) { return(""); } var linkentity = linkitem.name; if (linkentity == null) { return(null); } var attributeitems = linkitem.Items .OfType <FetchAttributeType>() .Where(i => i.name != null); if (linkitem.intersect) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; linkentity = nextlink.name; if (nextlink.Items == null) { return(null); } attributeitems = nextlink.Items .OfType <FetchAttributeType>() .Where(i => i.name != null); } } var resultList = GetAttributeNames(linkentity, attributeitems, sender); return(string.Join(",", resultList)); }
private static void removeUnnecessaryAttributesInner(FetchLinkEntityType link, savedqueryLayoutxmlGrid viewFeo) { if (link.Items != null) { List <object> array = link.Items.ToList(); for (int i = array.Count - 1; i > -1; i--) { object currentItem = array[i]; if (currentItem is FetchAttributeType) { FetchAttributeType attr = currentItem as FetchAttributeType; string alias = string.Format("{0}.{1}", link.alias, attr.name); if (!(viewFeo.row.cell.Where(x => x.name == attr.name).Count() > 0)) { array.RemoveAt(i); } } } link.Items = array.ToArray(); } }
private static FetchLinkEntityType getFechXmlFromLinks(FetchLinkEntityType fetchLinkType, string attributeName) { if (attributeName.Contains(".")) { if (attributeName.StartsWith(string.Concat(((FetchLinkEntityType)fetchLinkType).alias))) { return((FetchLinkEntityType)fetchLinkType); } if (fetchLinkType != null && fetchLinkType.Items != null && fetchLinkType.Items.Count() > 0) { foreach (object fetchType in fetchLinkType.Items) { if (fetchType is FetchLinkEntityType) { return(getFechXmlFromLinks((FetchLinkEntityType)fetchType, attributeName)); } } } } return(null); }
private static string GetNestedExpand(FetchLinkEntityType linkitem, FetchXmlBuilder sender, string entityname) { var resultList = new List <string>(); var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitem.intersect || linkitems.Count == 0) { return(""); } foreach (FetchLinkEntityType linkentityitem in linkitems) { var navigationProperty = LinkItemToNavigationProperty(entityname, linkentityitem, sender, out var child, out var manyToManyNextLink); var expandedSelect = GetExpandedSelect(linkentityitem, sender, linkentityitem.name); if (String.IsNullOrEmpty(expandedSelect)) { resultList.Add(navigationProperty); } else { resultList.Add(navigationProperty + "(" + expandedSelect + ")"); } } return(string.Join(",", resultList)); }
private static FetchAttributeType FindAliasedAttribute(object[] items, string colName, Func <FetchAttributeType, bool> predicate, ref FetchLinkEntityType linkEntity) { if (items == null) { return(null); } var match = items.OfType <FetchAttributeType>() .Where(a => a.alias == colName && (predicate == null || predicate(a))) .FirstOrDefault(); if (match != null) { return(match); } foreach (var le in items.OfType <FetchLinkEntityType>()) { match = FindAliasedAttribute(le.Items, colName, predicate, ref linkEntity); if (match != null) { if (linkEntity == null) { linkEntity = le; } return(match); } } return(null); }
// ReSharper disable once UnusedParameter.Local private static IEnumerable <FetchAttributeInfo> GetAllAttributes(string entityLogicalName, FetchLinkEntityType link) { var items = link.Items; if (items == null) { return(new List <FetchAttributeInfo>()); } return((from item in link.Items select(IEnumerable <FetchAttributeInfo>) GetAllAttributes(link.name, (dynamic)item)).SelectMany(v => v as FetchAttributeInfo[] ?? v.ToArray())); }
// ReSharper disable once UnusedParameter.Local private static void ProcessFetchXmlItem(LocalCrmDatabaseOrganizationService service, LinkEntity entityLink, FetchLinkEntityType link) { var joinType = link.linktype == "outer" ? JoinOperator.LeftOuter : JoinOperator.Inner; var childLink = new LinkEntity(entityLink.LinkToEntityName, link.name, link.@from, link.to, joinType) { EntityAlias = link.alias, }; entityLink.LinkEntities.Add(childLink); var items = link.Items; if (items == null) { return; } foreach (dynamic item in items) { ProcessFetchXmlItem(service, childLink, item); } }
internal FetchAttributeType AddAttribute(string colName, Func <FetchAttributeType, bool> predicate, IAttributeMetadataCache metadata, out bool added, out FetchLinkEntityType linkEntity) { var parts = colName.Split('.'); if (parts.Length == 1) { added = false; return(Entity.FindAliasedAttribute(colName, predicate, out linkEntity)); } var entityName = parts[0]; var attr = new FetchAttributeType { name = parts[1].ToLowerInvariant() }; if (Alias == entityName) { linkEntity = null; var meta = metadata[Entity.name].Attributes.SingleOrDefault(a => a.LogicalName == attr.name && a.AttributeOf == null); if (meta == null && (attr.name.EndsWith("name") || attr.name.EndsWith("type"))) { var logicalName = attr.name.Substring(0, attr.name.Length - 4); meta = metadata[Entity.name].Attributes.SingleOrDefault(a => a.LogicalName == logicalName && a.AttributeOf == null); if (meta != null) { attr.name = logicalName; } } if (Entity.Items != null) { var existing = Entity.Items.OfType <FetchAttributeType>().FirstOrDefault(a => a.name == attr.name || a.alias == attr.name); if (existing != null && (predicate == null || predicate(existing))) { added = false; return(existing); } } Entity.AddItem(attr); } else { linkEntity = Entity.FindLinkEntity(entityName); var meta = metadata[linkEntity.name].Attributes.SingleOrDefault(a => a.LogicalName == attr.name && a.AttributeOf == null); if (meta == null && (attr.name.EndsWith("name") || attr.name.EndsWith("type"))) { var logicalName = attr.name.Substring(0, attr.name.Length - 4); meta = metadata[linkEntity.name].Attributes.SingleOrDefault(a => a.LogicalName == logicalName && a.AttributeOf == null); if (meta != null) { attr.name = logicalName; } } if (linkEntity.Items != null) { var existing = linkEntity.Items.OfType <FetchAttributeType>().FirstOrDefault(a => a.name == attr.name || a.alias == attr.name); if (existing != null && (predicate == null || predicate(existing))) { added = false; return(existing); } } linkEntity.AddItem(attr); } added = true; return(attr); }
private static RelationshipMetadataBase LinkItemToRelation(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender) { GetEntityMetadata(entityname, sender); var entity = FetchXmlBuilder.entities[entityname]; foreach (var relation in entity.OneToManyRelationships) { if (relation.ReferencedEntity == entityname && relation.ReferencedAttribute == linkitem.to && relation.ReferencingEntity == linkitem.name && relation.ReferencingAttribute == linkitem.from) { return(relation); } } foreach (var relation in entity.ManyToOneRelationships) { if (relation.ReferencingEntity == entityname && relation.ReferencingAttribute == linkitem.to && relation.ReferencedEntity == linkitem.name && relation.ReferencedAttribute == linkitem.from) { return(relation); } } foreach (var relation in entity.ManyToManyRelationships) { if (relation.Entity1LogicalName == entityname && relation.Entity1IntersectAttribute == linkitem.from) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (nextlink.linktype == "outer") { throw new Exception("OData queries do not support outer joins"); } if (relation.Entity2LogicalName == nextlink.name && relation.Entity2IntersectAttribute == nextlink.to) { return(relation); } } } if (relation.Entity2LogicalName == entityname && relation.Entity2IntersectAttribute == linkitem.from) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (nextlink.linktype == "outer") { throw new Exception("OData queries do not support outer joins"); } if (relation.Entity1LogicalName == nextlink.name && relation.Entity1IntersectAttribute == nextlink.to) { return(relation); } } } } throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}"); }
private static void addNewAttributeToFech(FetchType fecthObj, string attributeName, ViewsRelationsObj currentEntity) { if (string.IsNullOrEmpty(currentEntity.relationAlias)) { FetchEntityType currentEt = fecthObj.Items.Where(x => x is FetchEntityType && ((FetchEntityType)x).name.Equals(currentEntity.entity, StringComparison.InvariantCultureIgnoreCase)).Select(x => (FetchEntityType)x).FirstOrDefault(); if (currentEt != null) { List <object> items = currentEt.Items.ToList(); FetchAttributeType attribute = items.Where(x => x is FetchAttributeType && ((FetchAttributeType)x).name.Equals(attributeName)).Select(x => (FetchAttributeType)x).FirstOrDefault(); if (attribute == null) { List <object> objectList = currentEt.Items.ToList(); objectList.Add(new FetchAttributeType() { name = attributeName }); currentEt.Items = objectList.ToArray(); } } } else { FetchEntityType currentEt = fecthObj.Items.Where(x => x is FetchEntityType).Select(x => (FetchEntityType)x).FirstOrDefault(); if (currentEt != null) { FetchLinkEntityType currentLink = currentEt.Items.Where(x => x is FetchLinkEntityType && ((FetchLinkEntityType)x).alias.Equals(currentEntity.relationAlias, StringComparison.InvariantCultureIgnoreCase)) .Select(x => (FetchLinkEntityType)x).FirstOrDefault(); if (currentLink != null) { List <object> items = currentLink.Items.ToList(); FetchAttributeType attribute = items.Where(x => x is FetchAttributeType && ((FetchAttributeType)x).name.Equals(attributeName)).Select(x => (FetchAttributeType)x).FirstOrDefault(); if (attribute == null) { List <object> objectList = currentLink.Items.ToList(); objectList.Add(new FetchAttributeType() { name = attributeName }); currentLink.Items = objectList.ToArray(); } } else { List <object> objectList = currentEt.Items.ToList(); objectList.Add(new FetchLinkEntityType() { Items = new object[] { (new FetchAttributeType() { name = attributeName }) }, name = currentEntity.entity, alias = currentEntity.relationAlias, linktype = "outer", from = currentEntity.relationFrom, to = currentEntity.relationTo, intersect = false, intersectSpecified = false, visible = false, visibleSpecified = false, }); currentEt.Items = objectList.ToArray(); } } } }
private static string LinkItemToNavigationProperty(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender, out bool child, out FetchLinkEntityType manyToManyNextLink) { manyToManyNextLink = null; GetEntityMetadata(entityname, sender); var entity = sender.entities[entityname]; foreach (var relation in entity.OneToManyRelationships .Where(r => r.ReferencedEntity == entityname && r.ReferencedAttribute == linkitem.to && r.ReferencingEntity == linkitem.name && r.ReferencingAttribute == linkitem.from)) { child = true; return(relation.ReferencedEntityNavigationPropertyName); } foreach (var relation in entity.ManyToOneRelationships .Where(r => r.ReferencingEntity == entityname && r.ReferencingAttribute == linkitem.to && r.ReferencedEntity == linkitem.name && r.ReferencedAttribute == linkitem.from)) { child = false; return(relation.ReferencingEntityNavigationPropertyName); } foreach (var relation in entity.ManyToManyRelationships .Where(r => r.Entity1LogicalName == entityname && r.Entity1IntersectAttribute == linkitem.from)) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (relation.Entity2LogicalName == nextlink.name && relation.Entity2IntersectAttribute == nextlink.to) { child = true; manyToManyNextLink = nextlink; return(relation.Entity1NavigationPropertyName); } } } foreach (var relation in entity.ManyToManyRelationships .Where(r => r.Entity2LogicalName == entityname && r.Entity2IntersectAttribute == linkitem.from)) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (relation.Entity1LogicalName == nextlink.name && relation.Entity1IntersectAttribute == nextlink.from) { child = true; manyToManyNextLink = nextlink; return(relation.Entity2NavigationPropertyName); } } } throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}"); }
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 } }
private static string LinkItemToNavigationProperty(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender, out bool child) { GetEntityMetadata(entityname, sender); var entity = sender.entities[entityname]; foreach (var relation in entity.OneToManyRelationships .Where(r => r.ReferencedEntity == entityname && r.ReferencedAttribute == linkitem.to && r.ReferencingEntity == linkitem.name && r.ReferencingAttribute == linkitem.from)) { if (linkitem.linktype != "outer") { throw new ApplicationException($"OData queries do not support inner joins on 1:N relationships. Try changing link to {linkitem.name} to an outer join"); } child = true; return(relation.ReferencedEntityNavigationPropertyName); } foreach (var relation in entity.ManyToOneRelationships .Where(r => r.ReferencingEntity == entityname && r.ReferencingAttribute == linkitem.to && r.ReferencedEntity == linkitem.name && r.ReferencedAttribute == linkitem.from)) { // OData $expand is equivalent to an outer join. Replicate inner join behaviour by adding a not-null filter on primary key // of related record type if (linkitem.linktype != "outer") { if (linkitem.Items == null) { linkitem.Items = new object[0]; } var filter = linkitem.Items.OfType <filter>().FirstOrDefault(f => f.type == filterType.and); if (filter == null) { filter = new filter { type = filterType.and, Items = new object[0] }; var items = new List <object>(linkitem.Items); items.Add(filter); linkitem.Items = items.ToArray(); } GetEntityMetadata(linkitem.name, sender); var linkedEntity = sender.entities[linkitem.name]; var condition = filter.Items.OfType <condition>() .FirstOrDefault(c => c.attribute == linkedEntity.PrimaryIdAttribute && c.@operator == @operator.notnull); if (condition == null) { condition = new condition { attribute = linkedEntity.PrimaryIdAttribute, @operator = @operator.notnull }; var items = new List <object>(filter.Items); items.Add(condition); filter.Items = items.ToArray(); } } child = false; return(relation.ReferencingEntityNavigationPropertyName); } foreach (var relation in entity.ManyToManyRelationships .Where(r => r.Entity1LogicalName == entityname && r.Entity1IntersectAttribute == linkitem.from)) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (nextlink.linktype != "outer") { throw new Exception($"OData queries do not support inner joins on N:N relationships. Try changing link to {nextlink.name} to an outer join"); } if (relation.Entity2LogicalName == nextlink.name && relation.Entity2IntersectAttribute == nextlink.to) { child = true; return(relation.Entity1NavigationPropertyName); } } } foreach (var relation in entity.ManyToManyRelationships .Where(r => r.Entity2LogicalName == entityname && r.Entity2IntersectAttribute == linkitem.from)) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (nextlink.linktype != "outer") { throw new Exception($"OData queries do not support inner joins on N:N relationships. Try changing link to {nextlink.name} to an outer join"); } if (relation.Entity1LogicalName == nextlink.name && relation.Entity1IntersectAttribute == nextlink.from) { child = true; return(relation.Entity2NavigationPropertyName); } } } throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}"); }
private void WriteLinkEntity(string parentEntityName, FetchLinkEntityType link) { bool isFirstLineWrited = false; WriteLine($"new {nameof(LinkEntity)}()"); WriteLine("{"); if (string.Equals(link.linktype, "outer", StringComparison.InvariantCulture)) { WriteLineIfHasLine(ref isFirstLineWrited); WriteLine($"{nameof(LinkEntity.JoinOperator)} = {nameof(JoinOperator)}.{nameof(JoinOperator.LeftOuter)},"); } WriteLineIfHasLine(ref isFirstLineWrited); WriteLine($"{nameof(LinkEntity.LinkFromEntityName)} = \"{parentEntityName}\","); WriteLine($"{nameof(LinkEntity.LinkFromAttributeName)} = \"{link.to}\","); WriteLine(); WriteLine($"{nameof(LinkEntity.LinkToEntityName)} = \"{link.name}\","); WriteLine($"{nameof(LinkEntity.LinkToAttributeName)} = \"{link.from}\","); if (!string.IsNullOrEmpty(link.alias) && !string.IsNullOrWhiteSpace(link.alias)) { WriteLine(); WriteLine($"{nameof(LinkEntity.EntityAlias)} = \"{link.alias}\","); } if (IsColumnSetNotDefault(link.Items)) { var columnDefinition = GetColumnSetDefinition(link.Items); WriteLine(); WriteLine($"{nameof(LinkEntity.Columns)} = {columnDefinition},"); } var criteria = link.Items.OfType <filter>().Where(IsFilterExpressionNotEmpty).ToList(); if (criteria.Any()) { filter filter; if (criteria.Count == 1) { filter = criteria[0]; } else { filter = new filter() { Items = criteria.ToArray(), }; } WriteLine(); WriteLine($"{nameof(LinkEntity.LinkCriteria)} ="); WriteLine("{"); WriteFilterExpressionContent(filter); WriteLine("},"); } if (link.Items.OfType <FetchLinkEntityType>().Any()) { WriteLine(); WriteLine($"{nameof(LinkEntity.LinkEntities)} ="); WriteLine("{"); bool isFirstLinkWrited = false; foreach (var sublink in link.Items.OfType <FetchLinkEntityType>()) { WriteLineIfHasLine(ref isFirstLinkWrited); WriteLinkEntity(link.name, sublink); } WriteLine("},"); } WriteLine("},"); }
public static FetchAttributeType FindAliasedAttribute(this FetchEntityType entity, string colName, Func <FetchAttributeType, bool> predicate, out FetchLinkEntityType linkEntity) { linkEntity = null; return(FindAliasedAttribute(entity.Items, colName, predicate, ref linkEntity)); }
public override IDataExecutionPlanNode FoldQuery(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints) { Source = Source.FoldQuery(dataSources, options, parameterTypes, hints); Source.Parent = this; // Foldable correlated IN queries "lefttable.column IN (SELECT righttable.column FROM righttable WHERE ...) are created as: // Filter: Expr2 is not null // -> FoldableJoin (LeftOuter SemiJoin) Expr2 = righttable.column in DefinedValues; righttable.column in RightAttribute // -> FetchXml // -> FetchXml (Distinct) orderby righttable.column // Foldable correlated EXISTS filters "EXISTS (SELECT * FROM righttable WHERE righttable.column = lefttable.column AND ...) are created as: // Filter - @var2 is not null // -> NestedLoop(LeftOuter SemiJoin), null join condition. Outer reference(lefttable.column -> @var1), Defined values(@var2 -> rightttable.primarykey) // -> FetchXml // -> Top 1 // -> Index spool, SeekValue @var1, KeyColumn rightttable.column // -> FetchXml var joins = new List <BaseJoinNode>(); var join = Source as BaseJoinNode; while (join != null) { joins.Add(join); if (join is MergeJoinNode && join.LeftSource is SortNode sort) { join = sort.Source as BaseJoinNode; } else { join = join.LeftSource as BaseJoinNode; } } var addedLinks = new List <FetchLinkEntityType>(); FetchXmlScan leftFetch; if (joins.Count == 0) { leftFetch = null; } else { var lastJoin = joins.Last(); if (lastJoin is MergeJoinNode && lastJoin.LeftSource is SortNode sort) { leftFetch = sort.Source as FetchXmlScan; } else { leftFetch = lastJoin.LeftSource as FetchXmlScan; } } while (leftFetch != null && joins.Count > 0) { join = joins.Last(); if (join.JoinType != QualifiedJoinType.LeftOuter || !join.SemiJoin) { break; } FetchLinkEntityType linkToAdd; string leftAlias; if (join is FoldableJoinNode merge) { // Check we meet all the criteria for a foldable correlated IN query var rightSort = join.RightSource as SortNode; var rightFetch = (rightSort?.Source ?? join.RightSource) as FetchXmlScan; if (rightFetch == null) { break; } if (!leftFetch.DataSource.Equals(rightFetch.DataSource, StringComparison.OrdinalIgnoreCase)) { break; } // Sorts could be folded into FetchXML or could be in separate node string attribute; if (rightSort != null) { if (rightSort.Sorts.Count != 1) { break; } if (!(rightSort.Sorts[0].Expression is ColumnReferenceExpression sortCol)) { break; } attribute = sortCol.GetColumnName(); } else { var rightSorts = (rightFetch.Entity.Items ?? Array.Empty <object>()).OfType <FetchOrderType>().ToList(); if (rightSorts.Count != 1) { break; } if (!String.IsNullOrEmpty(rightSorts[0].alias)) { break; } attribute = $"{rightFetch.Alias}.{rightSorts[0].attribute}"; } if (!merge.RightAttribute.GetColumnName().Equals(attribute, StringComparison.OrdinalIgnoreCase)) { break; } var rightSchema = rightFetch.GetSchema(dataSources, parameterTypes); // Right values need to be distinct - still allowed if it's the primary key if (!rightFetch.FetchXml.distinct && rightSchema.PrimaryKey != attribute) { break; } var definedValueName = join.DefinedValues.SingleOrDefault(kvp => kvp.Value == attribute).Key; if (definedValueName == null) { break; } var notNullFilter = FindNotNullFilter(Filter, definedValueName); if (notNullFilter == null) { break; } // We can fold IN to a simple left outer join where the attribute is the primary key if (!rightFetch.FetchXml.distinct && rightSchema.PrimaryKey == attribute) { // Replace the filter on the defined value name with a filter on the primary key column notNullFilter.Expression = attribute.ToColumnReference(); linkToAdd = new FetchLinkEntityType { name = rightFetch.Entity.name, alias = rightFetch.Alias, from = merge.RightAttribute.MultiPartIdentifier.Identifiers.Last().Value, to = merge.LeftAttribute.MultiPartIdentifier.Identifiers.Last().Value, linktype = "outer", Items = rightFetch.Entity.Items.Where(i => !(i is FetchOrderType)).ToArray() }; } else { // We need to use an "in" join type - check that's supported if (!options.JoinOperatorsAvailable.Contains(JoinOperator.Any)) { break; } // Remove the filter and replace with an "in" link-entity Filter = Filter.RemoveCondition(notNullFilter); linkToAdd = new FetchLinkEntityType { name = rightFetch.Entity.name, alias = rightFetch.Alias, from = merge.RightAttribute.MultiPartIdentifier.Identifiers.Last().Value, to = merge.LeftAttribute.MultiPartIdentifier.Identifiers.Last().Value, linktype = "in", Items = rightFetch.Entity.Items.Where(i => !(i is FetchOrderType)).ToArray() }; } leftAlias = merge.LeftAttribute.MultiPartIdentifier.Identifiers.Reverse().Skip(1).First().Value; // Remove the sort that has been merged into the left side too if (leftFetch.Entity.Items != null) { leftFetch.Entity.Items = leftFetch.Entity .Items .Where(i => !(i is FetchOrderType sort) || !sort.attribute.Equals(merge.LeftAttribute.MultiPartIdentifier.Identifiers.Last().Value, StringComparison.OrdinalIgnoreCase)) .ToArray(); } } else if (join is NestedLoopNode loop) { // Check we meet all the criteria for a foldable correlated IN query if (!options.JoinOperatorsAvailable.Contains(JoinOperator.Exists)) { break; } if (loop.JoinCondition != null || loop.OuterReferences.Count != 1 || loop.DefinedValues.Count != 1) { break; } if (!(join.RightSource is TopNode top)) { break; } if (!(top.Top is IntegerLiteral topLiteral) || topLiteral.Value != "1") { break; } if (!(top.Source is IndexSpoolNode indexSpool)) { break; } if (indexSpool.SeekValue != loop.OuterReferences.Single().Value) { break; } if (!(indexSpool.Source is FetchXmlScan rightFetch)) { break; } if (indexSpool.KeyColumn.Split('.').Length != 2 || !indexSpool.KeyColumn.Split('.')[0].Equals(rightFetch.Alias, StringComparison.OrdinalIgnoreCase)) { break; } var notNullFilter = FindNotNullFilter(Filter, loop.DefinedValues.Single().Key); if (notNullFilter == null) { break; } // Remove the filter and replace with an "exists" link-entity Filter = Filter.RemoveCondition(notNullFilter); linkToAdd = new FetchLinkEntityType { name = rightFetch.Entity.name, alias = rightFetch.Alias, from = indexSpool.KeyColumn.Split('.')[1], to = loop.OuterReferences.Single().Key.Split('.')[1], linktype = "exists", Items = rightFetch.Entity.Items }; leftAlias = loop.OuterReferences.Single().Key.Split('.')[0]; } else { // This isn't a type of join we can fold as a correlated IN/EXISTS join break; } // Remove any attributes from the new linkentity var tempEntity = new FetchEntityType { Items = new object[] { linkToAdd } }; foreach (var link in tempEntity.GetLinkEntities()) { link.Items = (link.Items ?? Array.Empty <object>()).Where(i => !(i is FetchAttributeType) && !(i is allattributes)).ToArray(); } if (leftAlias.Equals(leftFetch.Alias, StringComparison.OrdinalIgnoreCase)) { leftFetch.Entity.AddItem(linkToAdd); } else { leftFetch.Entity.FindLinkEntity(leftAlias).AddItem(linkToAdd); } addedLinks.Add(linkToAdd); joins.Remove(join); if (joins.Count == 0) { Source = leftFetch; leftFetch.Parent = this; } else { join = joins.Last(); if (join is MergeJoinNode && join.LeftSource is SortNode sort) { sort.Source = leftFetch; leftFetch.Parent = sort; } else { join.LeftSource = leftFetch; leftFetch.Parent = join; } } } // If we've got a filter matching a column and a variable (key lookup in a nested loop) from a table spool, replace it with a index spool if (Source is TableSpoolNode tableSpool) { var schema = Source.GetSchema(dataSources, parameterTypes); if (ExtractKeyLookupFilter(Filter, out var filter, out var indexColumn, out var seekVariable) && schema.ContainsColumn(indexColumn, out indexColumn)) { var spoolSource = tableSpool.Source; // Index spool requires non-null key values if (indexColumn != schema.PrimaryKey) { spoolSource = new FilterNode { Source = tableSpool.Source, Filter = new BooleanIsNullExpression { Expression = indexColumn.ToColumnReference(), IsNot = true } }.FoldQuery(dataSources, options, parameterTypes, hints); } Source = new IndexSpoolNode { Source = spoolSource, KeyColumn = indexColumn, SeekValue = seekVariable }; Filter = filter; } } // Find all the data source nodes we could fold this into. Include direct data sources, those from either side of an inner join, or the main side of an outer join foreach (var source in GetFoldableSources(Source)) { var schema = source.GetSchema(dataSources, parameterTypes); if (source is FetchXmlScan fetchXml && !fetchXml.FetchXml.aggregate) { if (!dataSources.TryGetValue(fetchXml.DataSource, out var dataSource)) { throw new NotSupportedQueryFragmentException("Missing datasource " + fetchXml.DataSource); } var additionalLinkEntities = new Dictionary <object, List <FetchLinkEntityType> >(); // If the criteria are ANDed, see if any of the individual conditions can be translated to FetchXML Filter = ExtractFetchXMLFilters(dataSource.Metadata, options, Filter, schema, null, fetchXml.Entity.name, fetchXml.Alias, fetchXml.Entity.Items, out var fetchFilter, additionalLinkEntities); if (fetchFilter != null) { fetchXml.Entity.AddItem(fetchFilter); foreach (var kvp in additionalLinkEntities) { if (kvp.Key is FetchEntityType e) { foreach (var le in kvp.Value) { fetchXml.Entity.AddItem(le); } } else { foreach (var le in kvp.Value) { ((FetchLinkEntityType)kvp.Key).AddItem(le); } } } } } if (source is MetadataQueryNode meta) { // If the criteria are ANDed, see if any of the individual conditions can be translated to the metadata query Filter = ExtractMetadataFilters(Filter, meta, options, out var entityFilter, out var attributeFilter, out var relationshipFilter); meta.Query.AddFilter(entityFilter); if (attributeFilter != null && meta.Query.AttributeQuery == null) { meta.Query.AttributeQuery = new AttributeQueryExpression(); } meta.Query.AttributeQuery.AddFilter(attributeFilter); if (relationshipFilter != null && meta.Query.RelationshipQuery == null) { meta.Query.RelationshipQuery = new RelationshipQueryExpression(); } meta.Query.RelationshipQuery.AddFilter(relationshipFilter); } } foreach (var addedLink in addedLinks) { addedLink.SemiJoin = true; } if (Filter == null) { return(Source); } return(this); }
private static string GetExpandedSelect(FetchLinkEntityType linkitem, string relation, FetchXmlBuilder sender) { if (linkitem.Items == null) { return ""; } var result = ""; var linkentity = linkitem.name; var attributeitems = linkitem.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList(); if (linkitem.intersect) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; linkentity = nextlink.name; attributeitems = nextlink.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList(); } } if (attributeitems.Count > 0) { foreach (FetchAttributeType attributeitem in attributeitems) { result += relation + "/" + LogicalToSchemaName(linkentity, attributeitem.name, sender) + ","; } } return result; }
private static RelationshipMetadataBase LinkItemToRelation(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender) { GetEntityMetadata(entityname, sender); var entity = FetchXmlBuilder.entities[entityname]; foreach (var relation in entity.OneToManyRelationships) { if (relation.ReferencedEntity == entityname && relation.ReferencedAttribute == linkitem.to && relation.ReferencingEntity == linkitem.name && relation.ReferencingAttribute == linkitem.from) { return relation; } } foreach (var relation in entity.ManyToOneRelationships) { if (relation.ReferencingEntity == entityname && relation.ReferencingAttribute == linkitem.to && relation.ReferencedEntity == linkitem.name && relation.ReferencedAttribute == linkitem.from) { return relation; } } foreach (var relation in entity.ManyToManyRelationships) { if (relation.Entity1LogicalName == entityname && relation.Entity1IntersectAttribute == linkitem.from) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (nextlink.linktype == "outer") { throw new Exception("OData queries do not support outer joins"); } if (relation.Entity2LogicalName == nextlink.name && relation.Entity2IntersectAttribute == nextlink.to) { return relation; } } } if (relation.Entity2LogicalName == entityname && relation.Entity2IntersectAttribute == linkitem.from) { var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList(); if (linkitems.Count > 1) { throw new Exception("Invalid M:M-relation definition for OData"); } if (linkitems.Count == 1) { var nextlink = (FetchLinkEntityType)linkitems[0]; if (nextlink.linktype == "outer") { throw new Exception("OData queries do not support outer joins"); } if (relation.Entity1LogicalName == nextlink.name && relation.Entity1IntersectAttribute == nextlink.to) { return relation; } } } } throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}"); }