internal override void PropagateDataTypesForOutSchema() { // Output for JOIN depends on type of the join // For LEFT join, attributes becomes non-nullable // For CROSS join or INNER join, attributes type remains unchanged after join if (Type == JoinType.Left) { var prevLeftSchema = InOperatorLeft.OutputSchema; var prevRightSchema = InOperatorRight.OutputSchema; var fieldMapping = new Dictionary <Field, Field>(); foreach (var outField in OutputSchema) { var inFieldMatches = InputSchema.Where(f => f.FieldAlias == outField.FieldAlias); Debug.Assert(InputSchema.Where(f => f.FieldAlias == outField.FieldAlias).Count() == 1); // must have match in IN for any OUT field var inField = inFieldMatches.First(); // we made it that left schema LEFT OUTTER JOIN with right schema always var isFromRight = !prevLeftSchema.Any(f => f.FieldAlias == inField.FieldAlias); // copy over the schema first outField.Copy(inField); // make adjustment for outter join if (inField is ValueField) { Debug.Assert(outField.GetType() == inField.GetType()); // join doesn't alter field types var outFieldSingleField = outField as ValueField; if (isFromRight && !TypeHelper.CanAssignNullToType(outFieldSingleField.FieldType)) { // make it nullable variant of the original type outFieldSingleField.FieldType = TypeHelper.GetNullableTypeForType(outFieldSingleField.FieldType); } } else { Debug.Assert(outField is EntityField); var outEntCapFields = (outField as EntityField).EncapsulatedFields; if (isFromRight) { foreach (var outEntCapField in outEntCapFields) { if (!TypeHelper.CanAssignNullToType(outEntCapField.FieldType)) { // make it nullable variant of the original type outEntCapField.FieldType = TypeHelper.GetNullableTypeForType(outEntCapField.FieldType); } } } } } } else { base.PropagateDataTypesForOutSchema(); } }
/// <summary> /// Given a set of columns, return the input columns that are needed to generate those output columns. /// </summary> IEnumerable <DataViewSchema.Column> ISchemaBoundRowMapper.GetDependenciesForNewColumns(IEnumerable <DataViewSchema.Column> dependingColumns) { if (dependingColumns.Count() == 0) { return(Enumerable.Empty <DataViewSchema.Column>()); } return(InputSchema.Where(col => _inputColIndices.Contains(col.Index))); }
/// <summary> /// Used by logic planner to update the list of ReferencedFields /// </summary> internal virtual void PropagateReferencedPropertiesForEntityFields() { // lift the referenced fields from the next layer of operators back to this one's output entity fields var referredFieldsInDownstreamOps = OutOperators.SelectMany(op => op.InputSchema) .Where(f => f is EntityField) .Cast <EntityField>() .GroupBy(f => f.FieldAlias) .ToDictionary(kv => kv.Key, kv => kv.SelectMany(fn => fn.ReferencedFieldAliases).Distinct().ToList()); foreach (var field in OutputSchema.Where(f => f is EntityField).Cast <EntityField>()) { Debug.Assert(referredFieldsInDownstreamOps.ContainsKey(field.FieldAlias)); field.AddReferenceFieldNames(referredFieldsInDownstreamOps[field.FieldAlias]); } // lift the referenced fields in the entity fields from output to input schema of this operator, if // applicable if ((InputSchema?.Count ?? 0) > 0) { // handle entity name renamed cases by creating a map from field alias before projection to after // projection var aliasMap = new Dictionary <String, String>(); if (this is ProjectionOperator) { var aliasMap1 = (this as ProjectionOperator).ProjectionMap .Where(u => OutputSchema.Where(n => n is EntityField).Any(k => k.FieldAlias == u.Key)); aliasMap = aliasMap1?.ToDictionary(n => n.Value.GetChildrenQueryExpressionType <QueryExpressionProperty>().First().VariableName, n => n.Key); } else { // create a dummy alias map ( A -> A, B -> B; not alias name modified) for non-projection operator aliasMap = OutputSchema.Where(n => n is EntityField).ToDictionary(n => n.FieldAlias, n => n.FieldAlias); } foreach (var field in InputSchema.Where(f => f is EntityField).Cast <EntityField>()) { var mappedAlias = (aliasMap.ContainsKey(field.FieldAlias) ?aliasMap[field.FieldAlias]:null); if (mappedAlias != null && referredFieldsInDownstreamOps.ContainsKey(mappedAlias)) { field.AddReferenceFieldNames(referredFieldsInDownstreamOps[mappedAlias]); } } var referredFieldsForUpstream = InputSchema .Where(f => f is EntityField).Cast <EntityField>() .ToDictionary(kv => kv.FieldAlias, kv => kv); // Some operators has additional fields may get referenced even they are not in output schema // Such as in WHERE or ORDER BY // Child logical operator class implement this and does the appending AppendReferencedProperties(referredFieldsForUpstream); } }
/// <summary> /// For adding to the maintained list of what entities are joint together by this operator /// </summary> /// <param name="entityAlias1">the alias on the left side of the join</param> /// <param name="entityAlias2">the alias on the right side of the join</param> internal void AddJoinPair(JoinKeyPair joinKeyPair) { Debug.Assert(InputSchema.Where(f => f is EntityField && f.FieldAlias == joinKeyPair.NodeAlias).Count() == 1); Debug.Assert(InputSchema.Where(f => f is EntityField && f.FieldAlias == joinKeyPair.RelationshipOrNodeAlias).Count() == 1); _joinPairs.Add(joinKeyPair); }