예제 #1
0
        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;
                    }
                }
            }
        }
예제 #2
0
        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.");
        }
예제 #3
0
        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;
                    }
                }
            }
        }
예제 #6
0
    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();
    }
예제 #7
0
        // <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;
        }
예제 #8
0
        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);
 }
예제 #11
0
        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);
        }