Beispiel #1
0
            /// <summary>
            /// Produce a tuple containing joined rows.
            /// </summary>
            /// <param name="left">Left row.</param>
            /// <param name="right">Right row.</param>
            /// <param name="leftKey">Key used to join left element.</param>
            /// <param name="rightKey">Key used to join right element.</param>
            /// <param name="result">Result change node; used for type information.</param>
            /// <returns>Result of joining the input rows.</returns>
            private PropagatorResult CreateResultTuple(Tuple <CompositeKey, PropagatorResult> left, Tuple <CompositeKey, PropagatorResult> right, ChangeNode result)
            {
                // using ref compare to avoid triggering value based
                CompositeKey leftKey  = left.Item1;
                CompositeKey rightKey = right.Item1;
                Dictionary <PropagatorResult, PropagatorResult> map = null;

                if (!object.ReferenceEquals(null, leftKey) &&
                    !object.ReferenceEquals(null, rightKey) &&
                    !object.ReferenceEquals(leftKey, rightKey))
                {
                    // Merge key values from the left and the right (since they're equal, there's a possibility we'll
                    // project values only from the left or the right hand side and lose important context.)
                    CompositeKey mergedKey = leftKey.Merge(m_parent.m_updateTranslator.KeyManager, rightKey);
                    // create a dictionary so that we can replace key values with merged key values (carrying context
                    // from both sides)
                    map = new Dictionary <PropagatorResult, PropagatorResult>();
                    for (int i = 0; i < leftKey.KeyComponents.Length; i++)
                    {
                        map[leftKey.KeyComponents[i]]  = mergedKey.KeyComponents[i];
                        map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i];
                    }
                }

                PropagatorResult[] joinRecordValues = new PropagatorResult[2];
                joinRecordValues[0] = left.Item2;
                joinRecordValues[1] = right.Item2;
                PropagatorResult join = PropagatorResult.CreateStructuralValue(joinRecordValues, (StructuralType)result.ElementType.EdmType, false);

                // replace with merged key values as appropriate
                if (null != map)
                {
                    PropagatorResult replacement;
                    join = join.Replace(original => map.TryGetValue(original, out replacement) ? replacement : original);
                }

                return(join);
            }
Beispiel #2
0
 /// <summary>
 /// See <see cref="LeftPlaceholder"></see>
 /// </summary>
 /// <param name="key"></param>
 /// <param name="mode"></param>
 /// <returns></returns>
 private PropagatorResult RightPlaceholder(CompositeKey key, PopulateMode mode)
 {
     return(PlaceholderPopulator.Populate(m_right.Placeholder, key, m_rightPlaceholderKey, mode, m_parent.UpdateTranslator));
 }
Beispiel #3
0
                /// <summary>
                /// Construct a new placeholder with the shape of the given placeholder. Key values are
                /// injected into the resulting place holder and default values are substituted with
                /// either propagator constants or progagator nulls depending on the mode established
                /// by the <paramref name="mode"/> flag.
                /// </summary>
                /// <remarks>
                /// The key is essentially an array of values. The key map indicates that for a particular
                /// placeholder an expression (keyMap.Keys) corresponds to some ordinal in the key array.
                /// </remarks>
                /// <param name="placeholder">Placeholder to clone</param>
                /// <param name="key">Key to substitute</param>
                /// <param name="placeholderKey">Key elements in the placeholder (ordinally aligned with 'key')</param>
                /// <param name="mode">Mode of operation.</param>
                /// <param name="translator">Translator context.</param>
                /// <returns>Cloned placeholder with key values</returns>
                internal static PropagatorResult Populate(PropagatorResult placeholder, CompositeKey key,
                                                          CompositeKey placeholderKey, PopulateMode mode, UpdateTranslator translator)
                {
                    EntityUtil.CheckArgumentNull(placeholder, "placeholder");
                    EntityUtil.CheckArgumentNull(key, "key");
                    EntityUtil.CheckArgumentNull(placeholderKey, "placeholderKey");
                    EntityUtil.CheckArgumentNull(translator, "translator");

                    // Figure out which flags to apply to generated elements.
                    bool            isNull   = mode == PopulateMode.NullModified || mode == PopulateMode.NullPreserve;
                    bool            preserve = mode == PopulateMode.NullPreserve || mode == PopulateMode.Unknown;
                    PropagatorFlags flags    = PropagatorFlags.NoFlags;

                    if (!isNull)
                    {
                        flags |= PropagatorFlags.Unknown;
                    }                                                  // only null values are known
                    if (preserve)
                    {
                        flags |= PropagatorFlags.Preserve;
                    }

                    PropagatorResult result = placeholder.Replace(node =>
                    {
                        // See if this is a key element
                        int keyIndex = -1;
                        for (int i = 0; i < placeholderKey.KeyComponents.Length; i++)
                        {
                            if (placeholderKey.KeyComponents[i] == node)
                            {
                                keyIndex = i;
                                break;
                            }
                        }

                        if (keyIndex != -1)
                        {
                            // Key value.
                            return(key.KeyComponents[keyIndex]);
                        }
                        else
                        {
                            // for simple entries, just return using the markup context for this
                            // populator
                            object value = isNull ? null : node.GetSimpleValue();
                            return(PropagatorResult.CreateSimpleValue(flags, value));
                        }
                    });

                    return(result);
                }
Beispiel #4
0
            /// <summary>
            /// Propagate all changes associated with a particular join key.
            /// </summary>
            /// <param name="key">Key.</param>
            /// <param name="result">Resulting changes are added to this result.</param>
            private void Propagate(CompositeKey key, ChangeNode result, JoinDictionary leftDeletes, JoinDictionary leftInserts,
                                   JoinDictionary rightDeletes, JoinDictionary rightInserts)
            {
                // Retrieve changes associates with this join key
                Tuple <CompositeKey, PropagatorResult> leftInsert  = null;
                Tuple <CompositeKey, PropagatorResult> leftDelete  = null;
                Tuple <CompositeKey, PropagatorResult> rightInsert = null;
                Tuple <CompositeKey, PropagatorResult> rightDelete = null;

                Ops input = Ops.Nothing;

                if (leftInserts.TryGetValue(key, out leftInsert))
                {
                    input |= Ops.LeftInsert;
                }
                if (leftDeletes.TryGetValue(key, out leftDelete))
                {
                    input |= Ops.LeftDelete;
                }
                if (rightInserts.TryGetValue(key, out rightInsert))
                {
                    input |= Ops.RightInsert;
                }
                if (rightDeletes.TryGetValue(key, out rightDelete))
                {
                    input |= Ops.RightDelete;
                }

                // Get propagation rules for the changes
                Ops insertRule = m_insertRules[input];
                Ops deleteRule = m_deleteRules[input];

                if (Ops.Unsupported == insertRule || Ops.Unsupported == deleteRule)
                {
                    // If no propagation rules are defined, it suggests an invalid workload (e.g.
                    // a required entity or relationship is missing). In general, such exceptions
                    // should be caught by the RelationshipConstraintValidator, but we defensively
                    // check for problems here regardless. For instance, a 0..1:1..1 self-assocation
                    // implied a stronger constraint that cannot be checked by RelationshipConstraintValidator.

                    // First gather state entries contributing to the problem
                    List <IEntityStateEntry> stateEntries = new List <IEntityStateEntry>();
                    Action <Tuple <CompositeKey, PropagatorResult> > addStateEntries = (r) =>
                    {
                        if (r != null)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(r.Item2, this.m_parent.m_updateTranslator,
                                                                                       this.m_parent.m_table));
                        }
                    };
                    addStateEntries(leftInsert);
                    addStateEntries(leftDelete);
                    addStateEntries(rightInsert);
                    addStateEntries(rightDelete);

                    throw EntityUtil.Update(Strings.Update_InvalidChanges, null, stateEntries);
                }

                // Where needed, substitute null/unknown placeholders. In some of the join propagation
                // rules, we handle the case where a side of the join is 'unknown', or where one side
                // of a join is comprised of an record containing only nulls. For instance, we may update
                // only one extent appearing in a row of a table (unknown), or; we may insert only
                // the left hand side of a left outer join, in which case the right hand side is 'null'.
                if (0 != (Ops.LeftUnknown & insertRule))
                {
                    leftInsert = Tuple.Create(key, LeftPlaceholder(key, PopulateMode.Unknown));
                }
                if (0 != (Ops.LeftUnknown & deleteRule))
                {
                    leftDelete = Tuple.Create(key, LeftPlaceholder(key, PopulateMode.Unknown));
                }
                if (0 != (Ops.RightNullModified & insertRule))
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullModified));
                }
                else if (0 != (Ops.RightNullPreserve & insertRule))
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullPreserve));
                }
                else if (0 != (Ops.RightUnknown & insertRule))
                {
                    rightInsert = Tuple.Create(key, RightPlaceholder(key, PopulateMode.Unknown));
                }

                if (0 != (Ops.RightNullModified & deleteRule))
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullModified));
                }
                else if (0 != (Ops.RightNullPreserve & deleteRule))
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.NullPreserve));
                }
                else if (0 != (Ops.RightUnknown & deleteRule))
                {
                    rightDelete = Tuple.Create(key, RightPlaceholder(key, PopulateMode.Unknown));
                }

                // Populate elements in join output
                if (null != leftInsert && null != rightInsert)
                {
                    result.Inserted.Add(CreateResultTuple(leftInsert, rightInsert, result));
                }
                if (null != leftDelete && null != rightDelete)
                {
                    result.Deleted.Add(CreateResultTuple(leftDelete, rightDelete, result));
                }
            }
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        {
            KeyManager   keyManager = compiler.m_translator.KeyManager;
            CompositeKey otherKey   = new CompositeKey(GetKeyConstants(other));

            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;

            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            {
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier;

                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                {
                    sharedPrincipal = false;
                }
            }

            if (sharedPrincipal)
            {
                // if the duplication is due to shared principals, there is a duplicate key exception
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                                   .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table));
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            {
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet <IEntityStateEntry> commonDependents = null;
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents))
                {
                    var dependents = new HashSet <IEntityStateEntry>();
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) &&
                            null != dependentResult.StateEntry)
                        {
                            dependents.Add(dependentResult.StateEntry);
                        }
                    }
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet <IEntityStateEntry>(dependents);
                    }
                    else
                    {
                        commonDependents.IntersectWith(dependents);
                    }
                }

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                                        EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents);
            }
        }