Esempio n. 1
0
        internal override void PropagateDataTypesForInSchema()
        {
            // Input for JOIN is the combination of both sources

            var prevLeftSchema  = InOperatorLeft.OutputSchema;
            var prevRightSchema = InOperatorRight.OutputSchema;
            var prevOutFields   = prevLeftSchema.Union(prevRightSchema);
            var fieldMapping    = new Dictionary <Field, Field>();

            foreach (var inF in InputSchema)
            {
                var matchOutFields = prevOutFields.Where(f => f.FieldAlias == inF.FieldAlias);
                if (matchOutFields.Count() > 1)
                {
                    // In case of join by node ids between left and right input (such as in MATCH .. OPTIONAL MATCH ... WHERE case)
                    // we will take the from left side and ignore duplications
                    // otherwise throws exception
                    if (!JoinPairs.All(jp => jp.Type == JoinKeyPair.JoinKeyPairType.NodeId))
                    {
                        throw new TranspilerInternalErrorException($"Ambiguous match of field with alias '{inF.FieldAlias}'");
                    }
                }
                if (matchOutFields.Count() == 0)
                {
                    throw new TranspilerInternalErrorException($"Failed to match field with alias '{inF.FieldAlias}'");
                }
                fieldMapping.Add(inF, matchOutFields.First());
            }

            CopyFieldInfoHelper(InputSchema, fieldMapping.Values);

            // additional validation on if relationships can satisfy the joins if directional traversal is explicitly specified
            foreach (var joinPair in JoinPairs)
            {
                var nodeEntity = InputSchema.First(s => s.FieldAlias == joinPair.NodeAlias) as EntityField;
                var relEntity  = InputSchema.First(s => s.FieldAlias == joinPair.RelationshipOrNodeAlias) as EntityField;
                switch (joinPair.Type)
                {
                case JoinKeyPair.JoinKeyPairType.Source:
                    if (relEntity.BoundSourceEntityName != nodeEntity.BoundEntityName)
                    {
                        throw new TranspilerBindingException($"Cannot find relationship ({nodeEntity.BoundEntityName})-[{relEntity.BoundSourceEntityName}]->(*), for {joinPair.NodeAlias} and {joinPair.RelationshipOrNodeAlias}");
                    }
                    break;

                case JoinKeyPair.JoinKeyPairType.Sink:
                    if (relEntity.BoundSinkEntityName != nodeEntity.BoundEntityName)
                    {
                        throw new TranspilerBindingException($"Cannot find relationship ({nodeEntity.BoundEntityName})<-[{relEntity.BoundSourceEntityName}]-(*), for {joinPair.NodeAlias} and {joinPair.RelationshipOrNodeAlias}");
                    }
                    break;

                default:
                    // in .Either, or .Both case
                    // no validation need to be done as Binding should already enforce the existence of the relationships
                    break;
                }
            }
        }
Esempio n. 2
0
        internal override void PropagateDateTypesForOutSchema()
        {
            // projection may alter the schema with calculated columns
            // we calculate the data type of all the fields in the output schema
            // using type evaluation method on the QueryExpression

            // Map from out_alias to { projection_expression,
            // e.g.: (a.b + c) AS d
            //    "d" -> (<expression of a.b+c>, <d's field in OutputSchema>)
            var exprToOutputMap = ProjectionMap.ToDictionary(
                kv => kv.Key, // key is output alias
                kv => new { Expr = kv.Value, Field = OutputSchema.First(f => f.FieldAlias == kv.Key) } // value is the corresponding field object and expression
                );

            foreach (var map in exprToOutputMap)
            {
                // toggle the fact if any of the output column requires aggregation
                HasAggregationField = HasAggregationField ||
                                      (map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionAggregationFunction>().Count() > 0);

                var allPropertyReferences = map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionProperty>();

                // update types for all QueryExpressionPropty object first based on InputSchema (so QueryExpression.EvaluteType() will work)
                foreach (var prop in allPropertyReferences)
                {
                    UpdatePropertyBasedOnAliasFromInputSchema(prop);
                }

                // then, update the type in the OutputSchema
                if (map.Value.Field is EntityField)
                {
                    // This can only be direct exposure of entity (as opposed to deference of a particular property)
                    // We just copy of the fields that the entity can potentially be dereferenced
                    Debug.Assert(allPropertyReferences.Count() == 1);
                    var varName         = allPropertyReferences.First().VariableName;
                    var matchInputField = InputSchema.First(f => f.FieldAlias == varName);
                    map.Value.Field.Copy(matchInputField);
                }
                else
                {
                    // This can be a complex expression involve multiple field/column references
                    // We will compute the type of the expression
                    Debug.Assert(map.Value.Field is ValueField);
                    var evalutedType = map.Value.Expr.EvaluateType();
                    var outField     = map.Value.Field as ValueField;
                    outField.FieldType = evalutedType;
                }
            }
        }
        internal override void PropagateDateTypesForOutSchema()
        {
            // projection may alter the schema with calculated columns
            // we calculate the data type of all the fields in the output schema
            // using type evaluation method on the QueryExpression

            var exprToOutputMap = ProjectionMap.ToDictionary(
                kv => kv.Key, // key is output alias
                kv => new { Expr = kv.Value, Field = OutputSchema.First(f => f.FieldAlias == kv.Key) } // value is the corresponding field object and expression
                );

            foreach (var map in exprToOutputMap)
            {
                // toggle the fact if any of the output column requires aggregation
                HasAggregationField = HasAggregationField ||
                                      (map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionAggregationFunction>().Count() > 0);

                var allPropertyReferences = map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionProperty>();
                if (map.Value.Field is EntityField)
                {
                    // This can only be direct exposure of entity (as opposed to deference of a particular property)
                    // We just copy of the fields that the entity can potentially be dereferenced
                    Debug.Assert(allPropertyReferences.Count() == 1);

                    var varName         = allPropertyReferences.First().VariableName;
                    var matchInputField = InputSchema.First(f => f.FieldAlias == varName);
                    map.Value.Field.Copy(matchInputField);
                }
                else
                {
                    // This can be a complex expression involve multiple field/column references
                    // We will compute the type of the expression
                    Debug.Assert(map.Value.Field is ValueField);

                    // first of all, bind the type to the variable references
                    foreach (var prop in allPropertyReferences)
                    {
                        var varName  = prop.VariableName;
                        var propName = prop.PropertyName;
                        Debug.Assert(prop.VariableName != null);
                        var matchedField = InputSchema.FirstOrDefault(f => f.FieldAlias == varName);

                        if (matchedField == null)
                        {
                            throw new TranspilerBindingException($"Failed to find input matching field alias {varName}");
                        }

                        if (string.IsNullOrEmpty(propName))
                        {
                            // direct reference to an alias (value column or entity column)
                            if (matchedField is ValueField)
                            {
                                prop.DataType = (matchedField as ValueField).FieldType;
                            }
                            else
                            {
                                // entity field reference in a single field expression
                                // this is valid only in handful situations, such as Count(d), Count(distinct(d))
                                // in such case, we populate the Entity object with correct entity type so that code generator can use it later
                                Debug.Assert(matchedField is EntityField);
                                var matchedEntity = matchedField as EntityField;
                                prop.Entity = matchedEntity.Type == EntityField.EntityType.Node ?
                                              new NodeEntity()
                                {
                                    EntityName = matchedEntity.EntityName, Alias = matchedEntity.FieldAlias
                                } as Entity:
                                new RelationshipEntity()
                                {
                                    EntityName = matchedEntity.EntityName, Alias = matchedEntity.FieldAlias
                                } as Entity;
                            }
                        }
                        else
                        {
                            // property dereference of an entity column
                            if (!(matchedField is EntityField))
                            {
                                throw new TranspilerBindingException($"Failed to dereference property {propName} for alias {varName}, which is not an alias of entity type as expected");
                            }
                            var entField     = matchedField as EntityField;
                            var entPropField = entField.EncapsulatedFields.FirstOrDefault(f => f.FieldAlias == propName);
                            if (entPropField == null)
                            {
                                throw new TranspilerBindingException($"Failed to dereference property {propName} for alias {varName}, Entity type {entField.BoundEntityName} does not have a property named {propName}");
                            }
                            entField.AddReferenceFieldName(propName);
                            prop.DataType = entPropField.FieldType;
                        }
                    }

                    // do data type evaluation
                    var evalutedType = map.Value.Expr.EvaluateType();
                    var outField     = map.Value.Field as ValueField;
                    outField.FieldType = evalutedType;
                }
            }
        }