Example #1
0
        /// <summary>
        ///     Builds an update command.
        /// </summary>
        /// <param name="oldRow"> Old value of the row being updated. </param>
        /// <param name="newRow"> New value for the row being updated. </param>
        /// <param name="processor"> Context for the table containing row. </param>
        /// <returns> Update command. </returns>
        internal UpdateCommand BuildUpdateCommand(
            PropagatorResult oldRow,
            PropagatorResult newRow, TableChangeProcessor processor)
        {
            // If we're updating a row, the row may not need to be touched (e.g., no concurrency validation required)
            var rowMustBeTouched = false;

            var target = GetTarget(processor);

            // Create set clauses and returning parameter
            Dictionary <int, string> outputIdentifiers;
            DbExpression             returning;
            var setClauses = new List <DbModificationClause>();

            foreach (var clause in BuildSetClauses(
                         target, newRow, oldRow, processor, /* insertMode */ false, out outputIdentifiers, out returning,
                         ref rowMustBeTouched))
            {
                setClauses.Add(clause);
            }

            // Construct predicate identifying the row to modify
            var predicate = BuildPredicate(target, oldRow, newRow, processor, ref rowMustBeTouched);

            if (0 == setClauses.Count)
            {
                if (rowMustBeTouched)
                {
                    var stateEntries = new List <IEntityStateEntry>();
                    stateEntries.AddRange(
                        SourceInterpreter.GetAllStateEntries(
                            oldRow, m_translator, processor.Table));
                    stateEntries.AddRange(
                        SourceInterpreter.GetAllStateEntries(
                            newRow, m_translator, processor.Table));
                    if (stateEntries.All(it => (it.State == EntityState.Unchanged)))
                    {
                        rowMustBeTouched = false;
                    }
                }

                // Determine if there is nothing to do (i.e., no values to set,
                // no computed columns, and no concurrency validation required)
                if (!rowMustBeTouched)
                {
                    return(null);
                }
            }

            // Initialize DML command tree
            var commandTree =
                new DbUpdateCommandTree(
                    m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate, setClauses.AsReadOnly(), returning);

            // Create command
            UpdateCommand command = new DynamicUpdateCommand(
                processor, m_translator, ModificationOperator.Update, oldRow, newRow, commandTree, outputIdentifiers);

            return(command);
        }
        internal static ReadOnlyCollection <IEntityStateEntry> GetAllStateEntries(
            PropagatorResult source,
            UpdateTranslator translator,
            EntitySet sourceTable)
        {
            SourceInterpreter sourceInterpreter = new SourceInterpreter(translator, sourceTable);

            sourceInterpreter.RetrieveResultMarkup(source);
            return(new ReadOnlyCollection <IEntityStateEntry>((IList <IEntityStateEntry>)sourceInterpreter.m_stateEntries));
        }
Example #3
0
        internal List <UpdateCommand> CompileCommands(
            ChangeNode changeNode,
            UpdateCompiler compiler)
        {
            Set <CompositeKey> keys = new Set <CompositeKey>(compiler.m_translator.KeyComparer);
            Dictionary <CompositeKey, PropagatorResult> dictionary1 = this.ProcessKeys(compiler, changeNode.Deleted, keys);
            Dictionary <CompositeKey, PropagatorResult> dictionary2 = this.ProcessKeys(compiler, changeNode.Inserted, keys);
            List <UpdateCommand> updateCommandList = new List <UpdateCommand>(dictionary1.Count + dictionary2.Count);

            foreach (CompositeKey key in keys)
            {
                PropagatorResult propagatorResult1;
                bool             flag1 = dictionary1.TryGetValue(key, out propagatorResult1);
                PropagatorResult propagatorResult2;
                bool             flag2 = dictionary2.TryGetValue(key, out propagatorResult2);
                try
                {
                    if (!flag1)
                    {
                        updateCommandList.Add(compiler.BuildInsertCommand(propagatorResult2, this));
                    }
                    else if (!flag2)
                    {
                        updateCommandList.Add(compiler.BuildDeleteCommand(propagatorResult1, this));
                    }
                    else
                    {
                        UpdateCommand updateCommand = compiler.BuildUpdateCommand(propagatorResult1, propagatorResult2, this);
                        if (updateCommand != null)
                        {
                            updateCommandList.Add(updateCommand);
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (ex.RequiresContext())
                    {
                        List <IEntityStateEntry> source = new List <IEntityStateEntry>();
                        if (propagatorResult1 != null)
                        {
                            source.AddRange((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(propagatorResult1, compiler.m_translator, this.m_table));
                        }
                        if (propagatorResult2 != null)
                        {
                            source.AddRange((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(propagatorResult2, compiler.m_translator, this.m_table));
                        }
                        throw new UpdateException(Strings.Update_GeneralExecutionException, ex, source.Cast <ObjectStateEntry>().Distinct <ObjectStateEntry>());
                    }
                    throw;
                }
            }
            return(updateCommandList);
        }
        // <summary>
        // Finds all markup associated with the given source.
        // </summary>
        // <param name="source"> Source expression. Must not be null. </param>
        // <param name="translator"> Translator containing session information. </param>
        // <param name="sourceTable"> Table from which the exception was thrown (must not be null). </param>
        // <returns> Markup. </returns>
        internal static ReadOnlyCollection<IEntityStateEntry> GetAllStateEntries(
            PropagatorResult source, UpdateTranslator translator,
            EntitySet sourceTable)
        {
            DebugCheck.NotNull(source);
            DebugCheck.NotNull(translator);
            DebugCheck.NotNull(sourceTable);

            var interpreter = new SourceInterpreter(translator, sourceTable);
            interpreter.RetrieveResultMarkup(source);

            return new ReadOnlyCollection<IEntityStateEntry>(interpreter.m_stateEntries);
        }
        /// <summary>
        ///     Finds all markup associated with the given source.
        /// </summary>
        /// <param name="source"> Source expression. Must not be null. </param>
        /// <param name="translator"> Translator containing session information. </param>
        /// <param name="sourceTable"> Table from which the exception was thrown (must not be null). </param>
        /// <returns> Markup. </returns>
        internal static ReadOnlyCollection <IEntityStateEntry> GetAllStateEntries(
            PropagatorResult source, UpdateTranslator translator,
            EntitySet sourceTable)
        {
            DebugCheck.NotNull(source);
            DebugCheck.NotNull(translator);
            DebugCheck.NotNull(sourceTable);

            var interpreter = new SourceInterpreter(translator, sourceTable);

            interpreter.RetrieveResultMarkup(source);

            return(new ReadOnlyCollection <IEntityStateEntry>(interpreter.m_stateEntries));
        }
Example #6
0
        internal override IList <IEntityStateEntry> GetStateEntries(
            UpdateTranslator translator)
        {
            List <IEntityStateEntry> entityStateEntryList = new List <IEntityStateEntry>(2);

            if (this.OriginalValues != null)
            {
                foreach (IEntityStateEntry allStateEntry in SourceInterpreter.GetAllStateEntries(this.OriginalValues, translator, this.Table))
                {
                    entityStateEntryList.Add(allStateEntry);
                }
            }
            if (this.CurrentValues != null)
            {
                foreach (IEntityStateEntry allStateEntry in SourceInterpreter.GetAllStateEntries(this.CurrentValues, translator, this.Table))
                {
                    entityStateEntryList.Add(allStateEntry);
                }
            }
            return((IList <IEntityStateEntry>)entityStateEntryList);
        }
Example #7
0
        internal UpdateCommand BuildUpdateCommand(
            PropagatorResult oldRow,
            PropagatorResult newRow,
            TableChangeProcessor processor)
        {
            bool rowMustBeTouched = false;
            DbExpressionBinding         target = UpdateCompiler.GetTarget(processor);
            List <DbModificationClause> modificationClauseList = new List <DbModificationClause>();
            Dictionary <int, string>    outputIdentifiers;
            DbExpression returning;

            foreach (DbModificationClause buildSetClause in this.BuildSetClauses(target, newRow, oldRow, processor, false, out outputIdentifiers, out returning, ref rowMustBeTouched))
            {
                modificationClauseList.Add(buildSetClause);
            }
            DbExpression predicate = this.BuildPredicate(target, oldRow, newRow, processor, ref rowMustBeTouched);

            if (modificationClauseList.Count == 0)
            {
                if (rowMustBeTouched)
                {
                    List <IEntityStateEntry> source = new List <IEntityStateEntry>();
                    source.AddRange((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(oldRow, this.m_translator, processor.Table));
                    source.AddRange((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(newRow, this.m_translator, processor.Table));
                    if (source.All <IEntityStateEntry>((Func <IEntityStateEntry, bool>)(it => it.State == EntityState.Unchanged)))
                    {
                        rowMustBeTouched = false;
                    }
                }
                if (!rowMustBeTouched)
                {
                    return((UpdateCommand)null);
                }
            }
            DbUpdateCommandTree updateCommandTree = new DbUpdateCommandTree(this.m_translator.MetadataWorkspace, DataSpace.SSpace, target, predicate, new ReadOnlyCollection <DbModificationClause>((IList <DbModificationClause>)modificationClauseList), returning);

            return((UpdateCommand) new DynamicUpdateCommand(processor, this.m_translator, ModificationOperator.Update, oldRow, newRow, (DbModificationCommandTree)updateCommandTree, outputIdentifiers));
        }
Example #8
0
        internal override IList <IEntityStateEntry> GetStateEntries(UpdateTranslator translator)
        {
            var stateEntries = new List <IEntityStateEntry>(2);

            if (null != OriginalValues)
            {
                foreach (var stateEntry in SourceInterpreter.GetAllStateEntries(
                             OriginalValues, translator, Table))
                {
                    stateEntries.Add(stateEntry);
                }
            }

            if (null != CurrentValues)
            {
                foreach (var stateEntry in SourceInterpreter.GetAllStateEntries(
                             CurrentValues, translator, Table))
                {
                    stateEntries.Add(stateEntry);
                }
            }
            return(stateEntries);
        }
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        {
            var keyManager = compiler.m_translator.KeyManager;
            var otherKey   = new CompositeKey(GetKeyConstants(other));

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

            for (var i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            {
                var identifier1 = key.KeyComponents[i].Identifier;
                var 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 new UpdateException(Strings.Update_DuplicateKeys, null, stateEntries.Cast <ObjectStateEntry>().Distinct());
            }
            else
            {
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet <IEntityStateEntry> commonDependents = null;
                foreach (var keyValue in key.KeyComponents.Concat(otherKey.KeyComponents))
                {
                    var dependents = new HashSet <IEntityStateEntry>();
                    foreach (var 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 new UpdateException(
                          Strings.Update_GeneralExecutionException,
                          new ConstraintException(Strings.Update_ReferentialConstraintIntegrityViolation),
                          commonDependents.Cast <ObjectStateEntry>().Distinct());
            }
        }
        // Processes all insert and delete requests in the table's <see cref="ChangeNode" />. Inserts
        // and deletes with the same key are merged into updates.
        internal List <UpdateCommand> CompileCommands(ChangeNode changeNode, UpdateCompiler compiler)
        {
            var keys = new Set <CompositeKey>(compiler.m_translator.KeyComparer);

            // Retrieve all delete results (original values) and insert results (current values) while
            // populating a set of all row keys. The set contains a single key per row.
            var deleteResults = ProcessKeys(compiler, changeNode.Deleted, keys);
            var insertResults = ProcessKeys(compiler, changeNode.Inserted, keys);

            var commands = new List <UpdateCommand>(deleteResults.Count + insertResults.Count);

            // Examine each row key to see if the row is being deleted, inserted or updated
            foreach (var key in keys)
            {
                PropagatorResult deleteResult;
                PropagatorResult insertResult;

                var hasDelete = deleteResults.TryGetValue(key, out deleteResult);
                var hasInsert = insertResults.TryGetValue(key, out insertResult);

                Debug.Assert(
                    hasDelete || hasInsert, "(update/TableChangeProcessor) m_keys must not contain a value " +
                    "if there is no corresponding insert or delete");

                try
                {
                    if (!hasDelete)
                    {
                        // this is an insert
                        commands.Add(compiler.BuildInsertCommand(insertResult, this));
                    }
                    else if (!hasInsert)
                    {
                        // this is a delete
                        commands.Add(compiler.BuildDeleteCommand(deleteResult, this));
                    }
                    else
                    {
                        // this is an update because it has both a delete result and an insert result
                        var updateCommand = compiler.BuildUpdateCommand(deleteResult, insertResult, this);
                        if (null != updateCommand)
                        {
                            // if null is returned, it means it is a no-op update
                            commands.Add(updateCommand);
                        }
                    }
                }
                catch (Exception e)
                {
                    if (e.RequiresContext())
                    {
                        // collect state entries in scope for the current compilation
                        var stateEntries = new List <IEntityStateEntry>();
                        if (null != deleteResult)
                        {
                            stateEntries.AddRange(
                                SourceInterpreter.GetAllStateEntries(
                                    deleteResult, compiler.m_translator, m_table));
                        }
                        if (null != insertResult)
                        {
                            stateEntries.AddRange(
                                SourceInterpreter.GetAllStateEntries(
                                    insertResult, compiler.m_translator, m_table));
                        }

                        throw new UpdateException(
                                  Strings.Update_GeneralExecutionException, e, stateEntries.Cast <ObjectStateEntry>().Distinct());
                    }
                    throw;
                }
            }

            return(commands);
        }
            // <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;

                var 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
                var insertRule = m_insertRules[input];
                var 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
                    var stateEntries = new List <IEntityStateEntry>();
                    Action <Tuple <CompositeKey, PropagatorResult> > addStateEntries = (r) =>
                    {
                        if (r != null)
                        {
                            stateEntries.AddRange(
                                SourceInterpreter.GetAllStateEntries(
                                    r.Item2, m_parent.m_updateTranslator,
                                    m_parent.m_table));
                        }
                    };
                    addStateEntries(leftInsert);
                    addStateEntries(leftDelete);
                    addStateEntries(rightInsert);
                    addStateEntries(rightDelete);

                    throw new UpdateException(Strings.Update_InvalidChanges, null, stateEntries.Cast <ObjectStateEntry>().Distinct());
                }

                // 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));
                }
            }
Example #12
0
        private void DiagnoseKeyCollision(
            UpdateCompiler compiler,
            PropagatorResult change,
            CompositeKey key,
            PropagatorResult other)
        {
            KeyManager   keyManager   = compiler.m_translator.KeyManager;
            CompositeKey compositeKey = new CompositeKey(this.GetKeyConstants(other));
            bool         flag         = true;

            for (int index = 0; flag && index < key.KeyComponents.Length; ++index)
            {
                int identifier1 = key.KeyComponents[index].Identifier;
                int identifier2 = compositeKey.KeyComponents[index].Identifier;
                if (!keyManager.GetPrincipals(identifier1).Intersect <int>(keyManager.GetPrincipals(identifier2)).Any <int>())
                {
                    flag = false;
                }
            }
            if (flag)
            {
                throw new UpdateException(Strings.Update_DuplicateKeys, (Exception)null, SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, this.m_table).Concat <IEntityStateEntry>((IEnumerable <IEntityStateEntry>)SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, this.m_table)).Cast <ObjectStateEntry>().Distinct <ObjectStateEntry>());
            }
            HashSet <IEntityStateEntry> source = (HashSet <IEntityStateEntry>)null;

            foreach (PropagatorResult propagatorResult in ((IEnumerable <PropagatorResult>)key.KeyComponents).Concat <PropagatorResult>((IEnumerable <PropagatorResult>)compositeKey.KeyComponents))
            {
                HashSet <IEntityStateEntry> entityStateEntrySet = new HashSet <IEntityStateEntry>();
                foreach (int dependent in keyManager.GetDependents(propagatorResult.Identifier))
                {
                    PropagatorResult owner;
                    if (keyManager.TryGetIdentifierOwner(dependent, out owner) && owner.StateEntry != null)
                    {
                        entityStateEntrySet.Add(owner.StateEntry);
                    }
                }
                if (source == null)
                {
                    source = new HashSet <IEntityStateEntry>((IEnumerable <IEntityStateEntry>)entityStateEntrySet);
                }
                else
                {
                    source.IntersectWith((IEnumerable <IEntityStateEntry>)entityStateEntrySet);
                }
            }
            throw new UpdateException(Strings.Update_GeneralExecutionException, (Exception) new ConstraintException(Strings.Update_ReferentialConstraintIntegrityViolation), source.Cast <ObjectStateEntry>().Distinct <ObjectStateEntry>());
        }