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> /// Constructs a join propagator. /// </summary> /// <param name="left"> Result of propagating changes in the left input to the join </param> /// <param name="right"> Result of propagating changes in the right input to the join </param> /// <param name="node"> Join operator in update mapping view over which to propagate changes </param> /// <param name="parent"> Handler of propagation for the entire update mapping view </param> internal JoinPropagator(ChangeNode left, ChangeNode right, DbJoinExpression node, Propagator parent) { DebugCheck.NotNull(left); DebugCheck.NotNull(right); DebugCheck.NotNull(node); DebugCheck.NotNull(parent); m_left = left; m_right = right; m_joinExpression = node; m_parent = parent; Debug.Assert( DbExpressionKind.LeftOuterJoin == node.ExpressionKind || DbExpressionKind.InnerJoin == node.ExpressionKind, "(Update/JoinPropagagtor/JoinEvaluator) " + "caller must ensure only left outer and inner joins are requested"); // Retrieve propagation rules for the join type of the expression. if (DbExpressionKind.InnerJoin == m_joinExpression.ExpressionKind) { m_insertRules = _innerJoinInsertRules; m_deleteRules = _innerJoinDeleteRules; } else { m_insertRules = _leftOuterJoinInsertRules; m_deleteRules = _leftOuterJoinDeleteRules; } // Figure out key selectors involved in the equi-join (if it isn't an equi-join, we don't support it) JoinConditionVisitor.GetKeySelectors(node.JoinCondition, out m_leftKeySelectors, out m_rightKeySelectors); // Find the key selector expressions in the left and right placeholders m_leftPlaceholderKey = ExtractKey(m_left.Placeholder, m_leftKeySelectors); m_rightPlaceholderKey = ExtractKey(m_right.Placeholder, m_rightKeySelectors); }
// <summary> // Constructs a join propagator. // </summary> // <param name="left"> Result of propagating changes in the left input to the join </param> // <param name="right"> Result of propagating changes in the right input to the join </param> // <param name="node"> Join operator in update mapping view over which to propagate changes </param> // <param name="parent"> Handler of propagation for the entire update mapping view </param> internal JoinPropagator(ChangeNode left, ChangeNode right, DbJoinExpression node, Propagator parent) { DebugCheck.NotNull(left); DebugCheck.NotNull(right); DebugCheck.NotNull(node); DebugCheck.NotNull(parent); m_left = left; m_right = right; m_joinExpression = node; m_parent = parent; Debug.Assert( DbExpressionKind.LeftOuterJoin == node.ExpressionKind || DbExpressionKind.InnerJoin == node.ExpressionKind, "(Update/JoinPropagagtor/JoinEvaluator) " + "caller must ensure only left outer and inner joins are requested"); // Retrieve propagation rules for the join type of the expression. if (DbExpressionKind.InnerJoin == m_joinExpression.ExpressionKind) { m_insertRules = _innerJoinInsertRules; m_deleteRules = _innerJoinDeleteRules; } else { m_insertRules = _leftOuterJoinInsertRules; m_deleteRules = _leftOuterJoinDeleteRules; } // Figure out key selectors involved in the equi-join (if it isn't an equi-join, we don't support it) JoinConditionVisitor.GetKeySelectors(node.JoinCondition, out m_leftKeySelectors, out m_rightKeySelectors); // Find the key selector expressions in the left and right placeholders m_leftPlaceholderKey = ExtractKey(m_left.Placeholder, m_leftKeySelectors); m_rightPlaceholderKey = ExtractKey(m_right.Placeholder, m_rightKeySelectors); }
/// <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 var leftKey = left.Item1; var rightKey = right.Item1; Dictionary<PropagatorResult, PropagatorResult> map = null; if (!ReferenceEquals(null, leftKey) && !ReferenceEquals(null, rightKey) && !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.) var 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 (var i = 0; i < leftKey.KeyComponents.Length; i++) { map[leftKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; } } var joinRecordValues = new PropagatorResult[2]; joinRecordValues[0] = left.Item2; joinRecordValues[1] = right.Item2; var 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; }
/// <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)); } }
// 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); }
// 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> // Produce a tuple containing joined rows. // </summary> // <param name="left"> Left row. </param> // <param name="right"> Right row. </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 var leftKey = left.Item1; var rightKey = right.Item1; Dictionary <PropagatorResult, PropagatorResult> map = null; if (!ReferenceEquals(null, leftKey) && !ReferenceEquals(null, rightKey) && !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.) var 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 (var i = 0; i < leftKey.KeyComponents.Length; i++) { map[leftKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; map[rightKey.KeyComponents[i]] = mergedKey.KeyComponents[i]; } } var joinRecordValues = new PropagatorResult[2]; joinRecordValues[0] = left.Item2; joinRecordValues[1] = right.Item2; var 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); }
// <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)); } }