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