public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) { if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace) { var queryCommand = interceptionContext.Result as DbQueryCommandTree; if (queryCommand != null) { var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor()); interceptionContext.Result = new DbQueryCommandTree( queryCommand.MetadataWorkspace, queryCommand.DataSpace, newQuery ); } var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree; if (deleteCommand != null) { var column = SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType); if (column != null) { var setClause = DbExpressionBuilder.SetClause( deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName).Property(column), DbExpression.FromBoolean(true)); var update = new DbUpdateCommandTree( deleteCommand.MetadataWorkspace, deleteCommand.DataSpace, deleteCommand.Target, deleteCommand.Predicate, new List<DbModificationClause> { setClause }.AsReadOnly(), null); interceptionContext.Result = update; } } } }
private void GetAffectedEntitySets(System.Data.Entity.Core.Common.CommandTrees.DbCommandTree commandTree) { System.Data.Entity.Core.Common.CommandTrees.DbExpressionVisitor visitor = new FindAffectedEntitySetsVisitor(this.affectedEntitySets, this.functionsUsed); System.Data.Entity.Core.Common.CommandTrees.DbQueryCommandTree queryTree = commandTree as System.Data.Entity.Core.Common.CommandTrees.DbQueryCommandTree; if (queryTree != null) { queryTree.Query.Accept(visitor); return; } System.Data.Entity.Core.Common.CommandTrees.DbFunctionCommandTree fxnTree = commandTree as System.Data.Entity.Core.Common.CommandTrees.DbFunctionCommandTree; if (fxnTree != null) { this.IsStoredProcedure = true; return; } System.Data.Entity.Core.Common.CommandTrees.DbUpdateCommandTree updateTree = commandTree as System.Data.Entity.Core.Common.CommandTrees.DbUpdateCommandTree; if (updateTree != null) { this.IsModification = true; updateTree.Target.Expression.Accept(visitor); updateTree.Predicate.Accept(visitor); if (updateTree.Returning != null) { updateTree.Returning.Accept(visitor); } return; } System.Data.Entity.Core.Common.CommandTrees.DbInsertCommandTree insertTree = commandTree as System.Data.Entity.Core.Common.CommandTrees.DbInsertCommandTree; if (insertTree != null) { this.IsModification = true; insertTree.Target.Expression.Accept(visitor); if (insertTree.Returning != null) { insertTree.Returning.Accept(visitor); } return; } System.Data.Entity.Core.Common.CommandTrees.DbDeleteCommandTree deleteTree = commandTree as System.Data.Entity.Core.Common.CommandTrees.DbDeleteCommandTree; if (deleteTree != null) { this.IsModification = true; deleteTree.Target.Expression.Accept(visitor); if (deleteTree.Predicate != null) { deleteTree.Predicate.Accept(visitor); } return; } throw new NotSupportedException("Command tree type " + commandTree.GetType() + " is not supported."); }
internal static string GenerateUpdateSql(DbUpdateCommandTree tree, out List<DbParameter> parameters, bool generateParameters = true) { StringBuilder commandText = new StringBuilder(CommandTextBuilderInitialCapacity); ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, generateParameters); bool first = true; commandText.Append("UPDATE "); tree.Target.Expression.Accept(translator); commandText.AppendLine(); // set c1 = ..., c2 = ..., ... commandText.Append("SET "); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); commandText.Append(" = "); setClause.Value.Accept(translator); translator.RegisterMemberValue(setClause.Property, setClause.Value); } if (first) { // If first is still true, it indicates there were no set // clauses. Introduce a fake set clause so that: // - we acquire the appropriate locks // - server-gen columns (e.g. timestamp) get recomputed EntitySetBase table = ((DbScanExpression)tree.Target.Expression).Target; // hope this column isn't indexed to not waste power EdmMember someColumn = table.ElementType.Members.Last(x => !MetadataHelpers.IsStoreGenerated(x)); commandText.AppendFormat("{0} = {0}", GenerateMemberSql(someColumn)); } commandText.AppendLine(); // where c1 = ..., c2 = ... commandText.Append("WHERE "); tree.Predicate.Accept(translator); commandText.AppendLine(); parameters = translator.Parameters; return commandText.ToString(); }
/// <summary>Implements the visitor pattern for the update command tree.</summary> /// <param name="updateTree">The update command tree.</param> protected virtual void VisitUpdateCommandTree(DbUpdateCommandTree updateTree) { Check.NotNull(updateTree, "updateTree"); VisitExpressionBindingPre(updateTree.Target); VisitModificationClauses(updateTree.SetClauses); VisitExpression(updateTree.Predicate); if (updateTree.Returning != null) { VisitExpression(updateTree.Returning); } VisitExpressionBindingPost(updateTree.Target); }
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) { if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace) { var queryCommand = interceptionContext.Result as DbQueryCommandTree; if (queryCommand != null) { var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor()); interceptionContext.Result = new DbQueryCommandTree( queryCommand.MetadataWorkspace, queryCommand.DataSpace, newQuery); } var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree; if (deleteCommand != null) { var column = SoftDeleteAttribute.GetSoftDeleteColumnName(deleteCommand.Target.VariableType.EdmType); if (column != null) { // Just because the entity has the soft delete annotation doesn't mean that // this particular table has the column. This occurs in situation like TPT // inheritance mapping and entity splitting where one type maps to multiple // tables. // If the table doesn't have the column we just want to leave the row unchanged // since it will be joined to the table that does have the column during query. // We can't no-op, so we just generate an UPDATE command that doesn't set anything. var setClauses = new List<DbModificationClause>(); var table = (EntityType)deleteCommand.Target.VariableType.EdmType; if (table.Properties.Any(p => p.Name == column)) { setClauses.Add(DbExpressionBuilder.SetClause( DbExpressionBuilder.Property(DbExpressionBuilder.Variable(deleteCommand.Target.VariableType, deleteCommand.Target.VariableName), column), DbExpression.FromBoolean(true))); } var update = new DbUpdateCommandTree( deleteCommand.MetadataWorkspace, deleteCommand.DataSpace, deleteCommand.Target, deleteCommand.Predicate, setClauses.AsReadOnly(), null); interceptionContext.Result = update; } } } }
internal static string GenerateUpdateSql(DbUpdateCommandTree tree, out List<DbParameter> parameters) { StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, "UpdateFunction"); // update [schemaName].[tableName] commandText.Append("UPDATE "); tree.Target.Expression.Accept(translator); commandText.AppendLine(); // set c1 = ..., c2 = ..., ... bool first = true; commandText.Append("SET "); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); commandText.Append(" = "); setClause.Value.Accept(translator); } if (first) { // If first is still true, it indicates there were no set // clauses. Introduce a fake set clause so that: // - we acquire the appropriate locks // - server-gen columns (e.g. timestamp) get recomputed // // We use the following pattern: // // update Foo // set @i = 0 // where ... DbParameter parameter = translator.CreateParameter(default(Int32), DbType.Int32); commandText.Append(parameter.ParameterName); commandText.Append(" = 0"); } commandText.AppendLine(); // where c1 = ..., c2 = ... commandText.Append("WHERE "); tree.Predicate.Accept(translator); commandText.AppendLine(); // generate returning sql GenerateReturningSql(commandText, tree, translator, tree.Returning); parameters = translator.Parameters; return commandText.ToString(); }
// <summary> // This method is added as a part of the fix for bug 13533 // In this method we try to see from the command tree whether there is any // updatable column(Property) available on the table(EntityType) // </summary> private static bool GetUpdatableColumn(DbUpdateCommandTree tree, out string updatableColumnName) { var result = false; updatableColumnName = ""; var entityType = (EntityType)tree.Target.VariableType.EdmType; foreach (var edmProperty in entityType.Properties) { if (entityType.KeyMembers.Contains(edmProperty.Name)) { // continue if it is a primary key continue; } if (RowversionString == edmProperty.TypeUsage.EdmType.Name) { // if the property is of type rowversion then we continue checking the next item in the list continue; } // check whether the property is a identity type if (edmProperty.TypeUsage.Facets.Contains(StoreGeneratedPatternString)) { var fct = edmProperty.TypeUsage.Facets.GetValue(StoreGeneratedPatternString, false); if (StoreGeneratedPattern.Identity == (StoreGeneratedPattern)fct.Value) { // continue to check for the next property if the current property is a identity continue; } } //if the column is found then return the column name string updatableColumnName = edmProperty.Name; result = true; break; } return result; }
internal static string GenerateUpdateSql(DbUpdateCommandTree tree, SqlVersion sqlVersion, out List<SqlParameter> parameters) { const string dummySetParameter = "@p"; var commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); var translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, sqlVersion); if (tree.SetClauses.Count == 0) { commandText.AppendLine("declare " + dummySetParameter + " int"); } // update [schemaName].[tableName] commandText.Append("update "); tree.Target.Expression.Accept(translator); commandText.AppendLine(); // set c1 = ..., c2 = ..., ... var first = true; commandText.Append("set "); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); commandText.Append(" = "); setClause.Value.Accept(translator); } if (first) { // If first is still true, it indicates there were no set // clauses. Introduce a fake set clause so that: // - we acquire the appropriate locks // - server-gen columns (e.g. timestamp) get recomputed // // We use the following pattern: // // update Foo // set @p = 0 // where ... commandText.Append(dummySetParameter + " = 0"); } commandText.AppendLine(); // where c1 = ..., c2 = ... commandText.Append("where "); tree.Predicate.Accept(translator); commandText.AppendLine(); // generate returning sql GenerateReturningSql(commandText, tree, null, translator, tree.Returning, false); parameters = translator.Parameters; return commandText.ToString(); }
private static DbModificationCommandTree RebuildCommandTree( DbModificationCommandTree originalTree, Dictionary<DbSetClause, DbSetClause> clauseMappings) { if (clauseMappings.Count == 0) { return originalTree; } DbModificationCommandTree result; Debug.Assert( originalTree.CommandTreeKind == DbCommandTreeKind.Insert || originalTree.CommandTreeKind == DbCommandTreeKind.Update, "Set clauses specified for a modification tree that is not an update or insert tree?"); if (originalTree.CommandTreeKind == DbCommandTreeKind.Insert) { var insertTree = (DbInsertCommandTree)originalTree; result = new DbInsertCommandTree( insertTree.MetadataWorkspace, insertTree.DataSpace, insertTree.Target, ReplaceClauses(insertTree.SetClauses, clauseMappings).AsReadOnly(), insertTree.Returning); } else { var updateTree = (DbUpdateCommandTree)originalTree; result = new DbUpdateCommandTree( updateTree.MetadataWorkspace, updateTree.DataSpace, updateTree.Target, updateTree.Predicate, ReplaceClauses(updateTree.SetClauses, clauseMappings).AsReadOnly(), updateTree.Returning); } return result; }
protected virtual void VisitUpdateCommandTree(DbUpdateCommandTree updateTree) { Contract.Requires(updateTree != null); VisitExpressionBindingPre(updateTree.Target); VisitModificationClauses(updateTree.SetClauses); VisitExpression(updateTree.Predicate); if (updateTree.Returning != null) { VisitExpression(updateTree.Returning); } VisitExpressionBindingPost(updateTree.Target); }
internal static string[] GenerateUpdateSql( DbUpdateCommandTree tree, out List<DbParameter> parameters, bool isLocalProvider, bool upperCaseKeywords = true) { var commandTexts = new List<String>(); var commandText = new SqlStringBuilder(CommandTextBuilderInitialCapacity) { UpperCaseKeywords = upperCaseKeywords }; var translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, isLocalProvider); // update [schemaName].[tableName] commandText.AppendKeyword("update "); tree.Target.Expression.Accept(translator); commandText.AppendLine(); // set c1 = ..., c2 = ..., ... var first = true; commandText.AppendKeyword("set "); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); commandText.Append(" = "); setClause.Value.Accept(translator); } if (first) { // If first is still true, it indicates there were no set // clauses. // - we acquire the appropriate locks // - server-gen columns (e.g. timestamp) get recomputed // // Fix #13533 : A fake update DML updating some column item // with the same value as before to acquire the lock on the table // while updating some columns in another table. This happens when // both the table are dependent on an entity and the members of entity // which is mapped to one table is being updated and the other table // needs to be locked for consistency. string updatableColumnName; if (GetUpdatableColumn(tree, out updatableColumnName)) { commandText.Append("["); commandText.Append(CommonUtils.EscapeSquareBraceNames(updatableColumnName)); commandText.Append("] "); commandText.Append(" = "); commandText.Append("["); commandText.Append(CommonUtils.EscapeSquareBraceNames(updatableColumnName)); commandText.Append("] "); } else { // Throw some meaningful error throw ADP1.Update( EntityRes.GetString(EntityRes.UpdateStatementCannotBeGeneratedForAcquiringLock), null); } } commandText.AppendLine(); // where c1 = ..., c2 = ... commandText.AppendKeyword("where "); tree.Predicate.Accept(translator); commandText.AppendLine(); commandTexts.Add(commandText.ToString()); commandText.Length = 0; // generate returning sql GenerateReturningSql(commandText, tree, translator, tree.Returning); if (!String.IsNullOrEmpty(commandText.ToString())) { commandTexts.Add(commandText.ToString()); } parameters = translator.Parameters; return commandTexts.ToArray(); }
/// <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; }
/// <summary> /// In case of an update command we always filter based on the userId /// </summary> static bool InterceptUpdate(DbCommandTreeInterceptionContext interceptionContext) { var updateCommand = interceptionContext.Result as DbUpdateCommandTree; if (updateCommand != null) { var column = UserAwareAttribute.GetUserColumnName(updateCommand.Target.VariableType.EdmType); if (!string.IsNullOrEmpty(column)) { // Get the userId (throw an exception if there is none) var userId = GetCurrentUserId(); // Create the variable reference in order to create the property var variableReference = DbExpressionBuilder.Variable(updateCommand.Target.VariableType, updateCommand.Target.VariableName); // Create the property to which will assign the correct value var userProperty = DbExpressionBuilder.Property(variableReference, column); // Create the userId where predicate, object representation of sql where userId = value statement var userIdWherePredicate = DbExpressionBuilder.Equal(userProperty, DbExpression.FromInt32(userId)); // Remove potential assignment of userId for extra safety var filteredSetClauses = updateCommand.SetClauses.Cast<DbSetClause>() .Where(sc => ((DbPropertyExpression)sc.Property).Property.Name != column); // Construct the final clauses, object representation of sql insert command values var finalSetClauses = new ReadOnlyCollection<DbModificationClause>(new List<DbModificationClause>(filteredSetClauses)); // The initial predicate is the sql where statement var initialPredicate = updateCommand.Predicate; // Add to the initial statement the userId statement which translates in sql AND UserId = 'value' var finalPredicate = initialPredicate.And(userIdWherePredicate); var newUpdateCommand = new DbUpdateCommandTree( updateCommand.MetadataWorkspace, updateCommand.DataSpace, updateCommand.Target, finalPredicate, finalSetClauses, updateCommand.Returning); interceptionContext.Result = newUpdateCommand; // True means an interception successfully happened so there is no need to continue return true; } } return false; }
private IEnumerable<CreateProcedureOperation> BuildCreateProcedureOperations( StorageEntityTypeModificationFunctionMapping modificationFunctionMapping, ModificationCommandTreeGenerator modificationCommandTreeGenerator, MigrationSqlGenerator migrationSqlGenerator) { DebugCheck.NotNull(modificationFunctionMapping); var insertCommandTrees = new DbInsertCommandTree[0]; var updateCommandTrees = new DbUpdateCommandTree[0]; var deleteCommandTrees = new DbDeleteCommandTree[0]; if (modificationCommandTreeGenerator != null) { var dynamicToFunctionModificationCommandConverter = new DynamicToFunctionModificationCommandConverter( modificationFunctionMapping, _target.StorageEntityContainerMapping); insertCommandTrees = dynamicToFunctionModificationCommandConverter .Convert( modificationCommandTreeGenerator .GenerateInsert(modificationFunctionMapping.EntityType.Identity)) .ToArray(); updateCommandTrees = dynamicToFunctionModificationCommandConverter .Convert( modificationCommandTreeGenerator .GenerateUpdate(modificationFunctionMapping.EntityType.Identity)) .ToArray(); deleteCommandTrees = dynamicToFunctionModificationCommandConverter .Convert( modificationCommandTreeGenerator .GenerateDelete(modificationFunctionMapping.EntityType.Identity)) .ToArray(); } string insertBodySql = null, updateBodySql = null, deleteBodySql = null; if (migrationSqlGenerator != null) { var providerManifestToken = _target.ProviderInfo.ProviderManifestToken; insertBodySql = migrationSqlGenerator .GenerateProcedureBody(insertCommandTrees, null, providerManifestToken); updateBodySql = migrationSqlGenerator.GenerateProcedureBody( updateCommandTrees, modificationFunctionMapping.UpdateFunctionMapping.RowsAffectedParameterName, providerManifestToken); deleteBodySql = migrationSqlGenerator.GenerateProcedureBody( deleteCommandTrees, modificationFunctionMapping.DeleteFunctionMapping.RowsAffectedParameterName, providerManifestToken); } yield return BuildCreateProcedureOperation( modificationFunctionMapping.InsertFunctionMapping.Function, insertBodySql); yield return BuildCreateProcedureOperation( modificationFunctionMapping.UpdateFunctionMapping.Function, updateBodySql); yield return BuildCreateProcedureOperation( modificationFunctionMapping.DeleteFunctionMapping.Function, deleteBodySql); }