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."); }
public void Visit_DbScanExpression_appends_SQL_if_defining_query_is_not_set() { var builder = new SqlStringBuilder(); var insertTree = new DbInsertCommandTree(); new DmlSqlGenerator.ExpressionTranslator( builder, insertTree, true, new SqlGenerator(SqlVersion.Sql10)) .Visit(CreateMockScanExpression(null).Object); Assert.Equal("[Kontainer].[Binky]", builder.ToString()); }
internal static string GenerateInsertSql(DbInsertCommandTree tree, out List<DbParameter> parameters) { StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, "InsertFunction"); // insert [schemaName].[tableName] commandText.Append("INSERT INTO "); tree.Target.Expression.Accept(translator); if (tree.SetClauses.Count > 0) { // (c1, c2, c3, ...) commandText.Append("("); bool first = true; foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); } commandText.AppendLine(")"); // values c1, c2, ... first = true; commandText.Append(" VALUES ("); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Value.Accept(translator); translator.RegisterMemberValue(setClause.Property, setClause.Value); } commandText.AppendLine(");"); } else // No columns specified. Insert an empty row containing default values by inserting null into the rowid { commandText.AppendLine(" DEFAULT VALUES;"); } // generate returning sql GenerateReturningSql(commandText, tree, translator, tree.Returning); parameters = translator.Parameters; return commandText.ToString(); }
/// <summary> /// Determine whether we should use a generated values variable to return server generated values. /// This is true when we're attempting to insert a row where the primary key is server generated /// but is not an integer type (and therefore can't be used with scope_identity()). It is also true /// where there is a compound server generated key. /// </summary> private static bool UseGeneratedValuesVariable(DbInsertCommandTree tree, SqlVersion sqlVersion) { var useGeneratedValuesVariable = false; if (sqlVersion > SqlVersion.Sql8 && tree.Returning != null) { // Figure out which columns have values var columnsWithValues = new HashSet<EdmMember>(tree.SetClauses.Cast<DbSetClause>().Select(s => ((DbPropertyExpression)s.Property).Property)); // Only SQL Server 2005+ support an output clause for inserts var firstKeyFound = false; foreach (var keyMember in ((DbScanExpression)tree.Target.Expression).Target.ElementType.KeyMembers) { if (!columnsWithValues.Contains(keyMember)) { if (firstKeyFound) { // compound server gen key useGeneratedValuesVariable = true; break; } else { firstKeyFound = true; if (!IsValidScopeIdentityColumnType(keyMember.TypeUsage)) { // unsupported type useGeneratedValuesVariable = true; break; } } } } } return useGeneratedValuesVariable; }
internal static string GenerateInsertSql(DbInsertCommandTree tree, SqlVersion sqlVersion, out List<SqlParameter> parameters) { var commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); var translator = new ExpressionTranslator( commandText, tree, null != tree.Returning, sqlVersion); var useGeneratedValuesVariable = UseGeneratedValuesVariable(tree, sqlVersion); var tableType = (EntityType)((DbScanExpression)tree.Target.Expression).Target.ElementType; if (useGeneratedValuesVariable) { // manufacture the variable, e.g. "declare @generated_values table(id uniqueidentifier)" commandText .Append("declare ") .Append(s_generatedValuesVariableName) .Append(" table("); var first = true; foreach (var column in tableType.KeyMembers) { if (first) { first = false; } else { commandText.Append(", "); } var columnType = SqlGenerator.GenerateSqlForStoreType(sqlVersion, column.TypeUsage); if (columnType == "rowversion" || columnType == "timestamp") { // rowversion and timestamp are intrinsically read-only. use binary to gather server generated // values for these types. columnType = "binary(8)"; } commandText .Append(GenerateMemberTSql(column)) .Append(" ") .Append(columnType); Facet collationFacet; if (column.TypeUsage.Facets.TryGetValue(DbProviderManifest.CollationFacetName, false, out collationFacet)) { var collation = collationFacet.Value as string; if (!string.IsNullOrEmpty(collation)) { commandText.Append(" collate ").Append(collation); } } } Debug.Assert(!first, "if useGeneratedValuesVariable is true, it implies some columns do not have values"); commandText.AppendLine(")"); } // insert [schemaName].[tableName] commandText.Append("insert "); tree.Target.Expression.Accept(translator); if (0 < tree.SetClauses.Count) { // (c1, c2, c3, ...) commandText.Append("("); var first = true; foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); } commandText.AppendLine(")"); } else { commandText.AppendLine(); } if (useGeneratedValuesVariable) { // output inserted.id into @generated_values commandText.Append("output "); var first = true; foreach (var column in tableType.KeyMembers) { if (first) { first = false; } else { commandText.Append(", "); } commandText.Append("inserted."); commandText.Append(GenerateMemberTSql(column)); } commandText .Append(" into ") .AppendLine(s_generatedValuesVariableName); } if (0 < tree.SetClauses.Count) { // values c1, c2, ... var first = true; commandText.Append("values ("); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Value.Accept(translator); translator.RegisterMemberValue(setClause.Property, setClause.Value); } commandText.AppendLine(")"); } else { // default values commandText.AppendLine("default values"); } // generate returning sql GenerateReturningSql(commandText, tree, tableType, translator, tree.Returning, useGeneratedValuesVariable); 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; }
internal static string GenerateInsertSql(DbInsertCommandTree 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("INSERT INTO "); tree.Target.Expression.Accept(translator); // (c1, c2, c3, ...) commandText.Append("("); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); } commandText.AppendLine(")"); // values c1, c2, ... first = true; commandText.Append("VALUES ("); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Value.Accept(translator); translator.RegisterMemberValue(setClause.Property, setClause.Value); } commandText.AppendLine(")"); parameters = translator.Parameters; return commandText.ToString(); }
protected virtual void VisitInsertCommandTree(DbInsertCommandTree insertTree) { Contract.Requires(insertTree != null); VisitExpressionBindingPre(insertTree.Target); VisitModificationClauses(insertTree.SetClauses); if (insertTree.Returning != null) { VisitExpression(insertTree.Returning); } VisitExpressionBindingPost(insertTree.Target); }
private string IntroduceRequiredLocalVariables(EntityType entityType, DbInsertCommandTree commandTree) { DebugCheck.NotNull(entityType); DebugCheck.NotNull(commandTree); var storeGeneratedKeys = entityType .KeyProperties .Where(p => p.IsStoreGeneratedIdentity) .ToList(); var sql = new SqlStringBuilder { UpperCaseKeywords = true }; if (storeGeneratedKeys.Any()) { foreach (var keyProperty in storeGeneratedKeys) { sql.Append(sql.Length == 0 ? "DECLARE " : ", "); sql.Append("@"); sql.Append(keyProperty.Name); sql.Append(" "); sql.Append(DmlSqlGenerator.GetVariableType(_sqlGenerator, keyProperty)); } sql.AppendLine(); var translator = new DmlSqlGenerator.ExpressionTranslator( sql, commandTree, true, _sqlGenerator, entityType.KeyProperties); DmlSqlGenerator.GenerateReturningSql( sql, commandTree, entityType, translator, commandTree.Returning, DmlSqlGenerator.UseGeneratedValuesVariable(commandTree, _sqlGenerator.SqlVersion)); sql.AppendLine(); sql.AppendLine(); } return sql.ToString(); }
internal static string[] GenerateInsertSql( DbInsertCommandTree tree, out List<DbParameter> parameters, bool isLocalProvider, bool upperCaseKeywords = true, bool createParameters = true) { var commandTexts = new List<String>(); var commandText = new SqlStringBuilder(CommandTextBuilderInitialCapacity) { UpperCaseKeywords = upperCaseKeywords }; var translator = new ExpressionTranslator( commandText, tree, null != tree.Returning, isLocalProvider, createParameters); // insert [schemaName].[tableName] commandText.AppendKeyword("insert "); tree.Target.Expression.Accept(translator); if (0 < tree.SetClauses.Count) { // (c1, c2, c3, ...) commandText.Append("("); var first = true; foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Property.Accept(translator); } commandText.AppendLine(")"); // values c1, c2, ... first = true; commandText.AppendKeyword("values ("); foreach (DbSetClause setClause in tree.SetClauses) { if (first) { first = false; } else { commandText.Append(", "); } setClause.Value.Accept(translator); translator.RegisterMemberValue(setClause.Property, setClause.Value); } commandText.AppendLine(")"); } else { // default values throw ADP1.NotSupported("Default values not supported"); } 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 insert command. /// </summary> /// <param name="newRow"> Row to insert. </param> /// <param name="processor"> Context for the table we're inserting into. </param> /// <returns> Insert command. </returns> internal UpdateCommand BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor) { // Bind the insert target var target = GetTarget(processor); // Create set clauses and returning parameter Dictionary<int, string> outputIdentifiers; DbExpression returning; var rowMustBeTouched = true; // for inserts, the row must always be touched var setClauses = new List<DbModificationClause>(); foreach (var clause in BuildSetClauses( target, newRow, null, processor, /* insertMode */ true, out outputIdentifiers, out returning, ref rowMustBeTouched)) { setClauses.Add(clause); } // Initialize DML command tree var commandTree = new DbInsertCommandTree(m_translator.MetadataWorkspace, DataSpace.SSpace, target, setClauses.AsReadOnly(), returning); // Create command UpdateCommand command = new DynamicUpdateCommand( processor, m_translator, ModificationOperator.Insert, null, newRow, commandTree, outputIdentifiers); return command; }
/// <summary> /// In case of an insert command we always assign the correct value to the userId /// </summary> static bool InterceptInsertCommand(DbCommandTreeInterceptionContext interceptionContext) { var insertCommand = interceptionContext.Result as DbInsertCommandTree; if (insertCommand != null) { var column = UserAwareAttribute.GetUserColumnName(insertCommand.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(insertCommand.Target.VariableType, insertCommand.Target.VariableName); // Create the property to which will assign the correct value var userProperty = DbExpressionBuilder.Property(variableReference, column); // Create the set clause, object representation of sql insert command var userSetClause = DbExpressionBuilder.SetClause(userProperty, DbExpression.FromInt32(userId)); // Remove potential assignment of userId for extra safety var filteredSetClauses = insertCommand.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) { userSetClause }); // Construct the new command var newInsertCommand = new DbInsertCommandTree( insertCommand.MetadataWorkspace, insertCommand.DataSpace, insertCommand.Target, finalSetClauses, insertCommand.Returning); interceptionContext.Result = newInsertCommand; // True means an interception successfully happened so there is no need to continue return true; } } return false; }
private IEnumerable<CreateProcedureOperation> BuildCreateProcedureOperations( StorageAssociationSetModificationFunctionMapping modificationFunctionMapping, ModificationCommandTreeGenerator modificationCommandTreeGenerator, MigrationSqlGenerator migrationSqlGenerator) { DebugCheck.NotNull(modificationFunctionMapping); var insertCommandTrees = new DbInsertCommandTree[0]; var deleteCommandTrees = new DbDeleteCommandTree[0]; if (modificationCommandTreeGenerator != null) { var dynamicToFunctionModificationCommandConverter = new DynamicToFunctionModificationCommandConverter( modificationFunctionMapping, _target.StorageEntityContainerMapping); insertCommandTrees = dynamicToFunctionModificationCommandConverter .Convert( modificationCommandTreeGenerator .GenerateAssociationInsert(modificationFunctionMapping.AssociationSet.ElementType.Identity)) .ToArray(); deleteCommandTrees = dynamicToFunctionModificationCommandConverter .Convert( modificationCommandTreeGenerator .GenerateAssociationDelete(modificationFunctionMapping.AssociationSet.ElementType.Identity)) .ToArray(); } string insertBodySql = null, deleteBodySql = null; if (migrationSqlGenerator != null) { var providerManifestToken = _target.ProviderInfo.ProviderManifestToken; insertBodySql = migrationSqlGenerator .GenerateProcedureBody(insertCommandTrees, null, providerManifestToken); deleteBodySql = migrationSqlGenerator.GenerateProcedureBody( deleteCommandTrees, modificationFunctionMapping.DeleteFunctionMapping.RowsAffectedParameterName, providerManifestToken); } yield return BuildCreateProcedureOperation( modificationFunctionMapping.InsertFunctionMapping.Function, insertBodySql); yield return BuildCreateProcedureOperation( modificationFunctionMapping.DeleteFunctionMapping.Function, deleteBodySql); }