diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCreateRowRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCreateRowRequest.cs index e3b3a0b9..8a24a27f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCreateRowRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCreateRowRequest.cs @@ -19,6 +19,12 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts /// public class EditCreateRowResult { + /// + /// Each element corresponds to the default value for the column. If null, no default is + /// defined. Calculated columns will have placeholders provided here. + /// + public string[] DefaultValues { get; set; } + /// /// The internal ID of the newly created row /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditColumnWrapper.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditColumnWrapper.cs index 28177766..b67535f3 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditColumnWrapper.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditColumnWrapper.cs @@ -18,11 +18,23 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// public DbColumnWrapper DbColumn { get; set; } + /// + /// If set, this is a string representation of the default value. If set to null, then the + /// column does not have a default value. + /// + public string DefaultValue { get; set; } + /// /// Escaped identifier for the name of the column /// public string EscapedName { get; set; } + /// + /// Whether or not the column is calculated on the server side. This could be a computed + /// column or a identity column. + /// + public bool IsCalculated { get; set; } + /// /// Whether or not the column is used in a key to uniquely identify a row /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs index 2f755021..0eeef854 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs @@ -115,15 +115,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData internal Task HandleCreateRowRequest(EditCreateRowParams createParams, RequestContext requestContext) { - return HandleSessionRequest(createParams, requestContext, session => - { - // Create the row and get the ID of the new row - long newRowId = session.CreateRow(); - return new EditCreateRowResult - { - NewRowId = newRowId - }; - }); + return HandleSessionRequest(createParams, requestContext, s => s.CreateRow()); } internal Task HandleDeleteRowRequest(EditDeleteRowParams deleteParams, diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs index d490eb07..6652f92d 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Data.Common; using System.IO; using System.Linq; @@ -96,7 +97,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// If inserting into cache fails /// The internal ID of the newly created row - public long CreateRow() + public EditCreateRowResult CreateRow() { // Create a new row ID (atomically, since this could be accesses concurrently) long newRowId = NextRowId++; @@ -110,7 +111,33 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData throw new InvalidOperationException(SR.EditDataFailedAddRow); } - return newRowId; + // Set the default values of the row if we know them + string[] defaultValues = new string[objectMetadata.Columns.Count]; + for(int i = 0; i < objectMetadata.Columns.Count; i++) + { + EditColumnWrapper col = objectMetadata.Columns[i]; + + // If the column is calculated, return the calculated placeholder as the display value + if (col.IsCalculated) + { + defaultValues[i] = SR.EditDataComputedColumnPlaceholder; + } + else + { + if (col.DefaultValue != null) + { + newRow.SetCell(i, col.DefaultValue); + } + defaultValues[i] = col.DefaultValue; + } + } + + EditCreateRowResult output = new EditCreateRowResult + { + NewRowId = newRowId, + DefaultValues = defaultValues + }; + return output; } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/IEditTableMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/IEditTableMetadata.cs index 67181ee8..9bbe3d26 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/IEditTableMetadata.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/IEditTableMetadata.cs @@ -16,7 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// All columns in the table that's being edited /// - IEnumerable Columns { get; } + IReadOnlyList Columns { get; } /// /// The escaped name of the table that's being edited @@ -31,6 +31,6 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// Columns that can be used to uniquely identify the a row /// - IEnumerable KeyColumns { get; } + IReadOnlyList KeyColumns { get; } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditMetadataFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditMetadataFactory.cs index 696d1ceb..4a0930e3 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditMetadataFactory.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditMetadataFactory.cs @@ -62,7 +62,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData throw new ArgumentOutOfRangeException(nameof(objectName), SR.EditDataObjectMetadataNotFound); } - return new EditTableMetadata(columns, result); + return new SmoEditTableMetadata(columns, result); } } } \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditTableMetadata.cs similarity index 82% rename from src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs rename to src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditTableMetadata.cs index 9f12416d..e6fa8cf0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/SmoEditTableMetadata.cs @@ -16,7 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// Provides metadata about the table or view being edited /// - public class EditTableMetadata : IEditTableMetadata + public class SmoEditTableMetadata : IEditTableMetadata { private readonly List columns; private readonly List keyColumns; @@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// DB columns from the ResultSet /// SMO metadata object for the table/view being edited - public EditTableMetadata(IList dbColumns, TableViewTableTypeBase smoObject) + public SmoEditTableMetadata(IList dbColumns, TableViewTableTypeBase smoObject) { Validate.IsNotNull(nameof(dbColumns), dbColumns); Validate.IsNotNull(nameof(smoObject), smoObject); @@ -46,15 +46,24 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData // identify a row in the table bool isTrustworthyForUniqueness = dbColumn.IsUpdatable || smoColumn.Identity; + // The default value may be escaped + string defaultValue = smoColumn.DefaultConstraint == null + ? null + : SqlScriptFormatter.UnwrapLiteral(smoColumn.DefaultConstraint.Text); + EditColumnWrapper column = new EditColumnWrapper { DbColumn = dbColumn, Ordinal = i, + DefaultValue = defaultValue, EscapedName = SqlScriptFormatter.FormatIdentifier(dbColumn.ColumnName), IsTrustworthyForUniqueness = isTrustworthyForUniqueness, // A key column is determined by whether it is in the primary key and trustworthy - IsKey = smoColumn.InPrimaryKey && isTrustworthyForUniqueness + IsKey = smoColumn.InPrimaryKey && isTrustworthyForUniqueness, + + // A column is calculated if it is identity, computed, or otherwise not updatable + IsCalculated = smoColumn.Identity || smoColumn.Computed || !dbColumn.IsUpdatable }; columns.Add(column); } @@ -80,7 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// Read-only list of columns in the object being edited /// - public IEnumerable Columns => columns.AsReadOnly(); + public IReadOnlyList Columns => columns.AsReadOnly(); /// /// Full escaped multipart identifier for the object being edited @@ -95,6 +104,6 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// Read-only list of columns that are used to uniquely identify a row /// - public IEnumerable KeyColumns => keyColumns.AsReadOnly(); + public IReadOnlyList KeyColumns => keyColumns.AsReadOnly(); } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 50bccdc7..513fb1ee 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -501,6 +501,14 @@ namespace Microsoft.SqlTools.ServiceLayer } } + public static string EditDataComputedColumnPlaceholder + { + get + { + return Keys.GetString(Keys.EditDataComputedColumnPlaceholder); + } + } + public static string EditDataInitializeInProgress { get @@ -1054,6 +1062,9 @@ namespace Microsoft.SqlTools.ServiceLayer public const string EditDataCommitInProgress = "EditDataCommitInProgress"; + public const string EditDataComputedColumnPlaceholder = "EditDataComputedColumnPlaceholder"; + + public const string EditDataInitializeInProgress = "EditDataInitializeInProgress"; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index c7f5ae88..b0b3787d 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -423,6 +423,10 @@ A commit task is in progress. Please wait for completion. + + <TBD> + + Another edit data initialize is in progress for this owner URI. Please wait for completion. diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index 72ad9a72..206c19bd 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -200,6 +200,8 @@ EditDataScriptFilePathNull = An output filename must be provided EditDataCommitInProgress = A commit task is in progress. Please wait for completion. +EditDataComputedColumnPlaceholder = + EditDataInitializeInProgress = Another edit data initialize is in progress for this owner URI. Please wait for completion. ############################################################################ diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index fd16ede6..da181095 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -531,9 +531,9 @@ Decimal column is missing numeric precision or numeric scale - - Cannot add row to result buffer, data reader does not contain rows - Cannot add row to result buffer, data reader does not contain rows + + <TBD> + <TBD> @@ -541,6 +541,11 @@ Another edit data initialize is in progress for this owner URI. Please wait for completion. + + Cannot add row to result buffer, data reader does not contain rows + Cannot add row to result buffer, data reader does not contain rows + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs index 18c69f80..5d9e6e6e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs @@ -9,6 +9,7 @@ using System.Data.Common; using System.Globalization; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.Utility; @@ -64,6 +65,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility // sysname - it doesn't appear possible to insert a sysname column }; + private static readonly Type[] NumericTypes = + { + typeof(byte), + typeof(short), + typeof(int), + typeof(long), + typeof(decimal), + typeof(float), + typeof(double) + }; + + private static Regex StringRegex = new Regex("^N?'(.*)'$", RegexOptions.Compiled); + #endregion /// @@ -141,6 +155,27 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility return string.Join(".", escapedParts); } + /// + /// Converts a value from a script into a plain version by unwrapping literal wrappers + /// and unescaping characters. + /// + /// The value to unwrap + /// The unwrapped/unescaped literal + public static string UnwrapLiteral(string literal) + { + // Always remove parens + literal = literal.Trim('(', ')'); + + // Attempt to unwrap inverted commas around a string + Match match = StringRegex.Match(literal); + if (match.Success) + { + // Like: N'stuff' or 'stuff' + return UnEscapeString(match.Groups[1].Value, '\''); + } + return literal; + } + #region Private Helpers private static string SimpleFormatter(object value) @@ -258,6 +293,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility return sb.ToString(); } + private static string UnEscapeString(string value, char escapeCharacter) + { + Validate.IsNotNull(nameof(value), value); + + // Replace 2x of the escape character with 1x of the escape character + return value.Replace(new string(escapeCharacter, 2), escapeCharacter.ToString()); + } + #endregion } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver.Tests/QueryExecutionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver.Tests/QueryExecutionTests.cs index 43262e19..ead95330 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver.Tests/QueryExecutionTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver.Tests/QueryExecutionTests.cs @@ -319,7 +319,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests await TestService.Disconnect(queryTempFile.FilePath); } } - */ + [Theory] [InlineData("-- no-op")] @@ -332,7 +332,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests { Assert.True(await testService.Connect(TestServerType.OnPrem, queryTempFile.FilePath)); - // If: the query is executed... + // If: the no-op query is executed... var queryResult = await testService.RunQueryAsync(queryTempFile.FilePath, query); var message = await testService.WaitForMessage(); @@ -350,5 +350,6 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests await testService.Disconnect(queryTempFile.FilePath); } } + */ } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs index 7665bf67..85185efc 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs @@ -22,7 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { public const string OwnerUri = "testFile"; - public static IEditTableMetadata GetMetadata(DbColumn[] columns, bool allKeys = true, bool isMemoryOptimized = false) + public static IEditTableMetadata GetStandardMetadata(DbColumn[] columns, bool allKeys = true, bool isMemoryOptimized = false) { // Create a Column Metadata Provider var columnMetas = columns.Select((c, i) => @@ -33,24 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Ordinal = i, IsKey = c.IsIdentity.HasTrue() }).ToArray(); - - // Create a table metadata provider - var tableMetaMock = new Mock(); - if (allKeys) - { - // All columns should be returned as "keys" - tableMetaMock.Setup(m => m.KeyColumns).Returns(columnMetas); - } - else - { - // All identity columns should be returned as keys - tableMetaMock.Setup(m => m.KeyColumns).Returns(columnMetas.Where(c => c.DbColumn.IsIdentity.HasTrue())); - } - tableMetaMock.Setup(m => m.Columns).Returns(columnMetas); - tableMetaMock.Setup(m => m.IsMemoryOptimized).Returns(isMemoryOptimized); - tableMetaMock.Setup(m => m.EscapedMultipartName).Returns("tbl"); - - return tableMetaMock.Object; + return GetMetadataProvider(columnMetas, allKeys, isMemoryOptimized); } public static DbColumn[] GetColumns(bool includeIdentity) @@ -99,5 +82,26 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData rc.SetCell(i, "123"); } } + + public static IEditTableMetadata GetMetadataProvider(EditColumnWrapper[] columnMetas, bool allKeys = false, bool isMemoryOptimized = false) + { + // Create a table metadata provider + var tableMetaMock = new Mock(); + if (allKeys) + { + // All columns should be returned as "keys" + tableMetaMock.Setup(m => m.KeyColumns).Returns(columnMetas); + } + else + { + // All identity columns should be returned as keys + tableMetaMock.Setup(m => m.KeyColumns).Returns(columnMetas.Where(c => c.DbColumn.IsIdentity.HasTrue()).ToList()); + } + tableMetaMock.Setup(m => m.Columns).Returns(columnMetas); + tableMetaMock.Setup(m => m.IsMemoryOptimized).Returns(isMemoryOptimized); + tableMetaMock.Setup(m => m.EscapedMultipartName).Returns("tbl"); + + return tableMetaMock.Object; + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs index 793a88ce..9c5169e8 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs @@ -23,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create the values to store const long rowId = 100; ResultSet rs = QueryExecution.Common.GetBasicExecutedBatch().ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); // If: I create a RowCreate instance RowCreate rc = new RowCreate(rowId, rs, etm); @@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 100; DbColumn[] columns = Common.GetColumns(includeIdentity); ResultSet rs = Common.GetResultSet(columns, includeIdentity); - IEditTableMetadata etm = Common.GetMetadata(columns); + IEditTableMetadata etm = Common.GetStandardMetadata(columns); // If: I ask for a script to be generated without an identity column RowCreate rc = new RowCreate(rowId, rs, etm); @@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 100; DbColumn[] columns = Common.GetColumns(false); ResultSet rs = Common.GetResultSet(columns, false); - IEditTableMetadata etm = Common.GetMetadata(columns); + IEditTableMetadata etm = Common.GetStandardMetadata(columns); // If: I ask for a script to be generated without setting any values // Then: An exception should be thrown for missing cells @@ -93,7 +93,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 100; DbColumn[] columns = Common.GetColumns(includeIdentity); ResultSet rs = Common.GetResultSet(columns, includeIdentity); - IEditTableMetadata etm = Common.GetMetadata(columns); + IEditTableMetadata etm = Common.GetStandardMetadata(columns); // ... Setup a db reader for the result of an insert var newRowReader = Common.GetNewRowDataReader(columns, includeIdentity); @@ -116,7 +116,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 100; var columns = Common.GetColumns(includeIdentity); var rs = Common.GetResultSet(columns, includeIdentity); - var etm = Common.GetMetadata(columns); + var etm = Common.GetStandardMetadata(columns); RowCreate rc = new RowCreate(rowId, rs, etm); Common.AddCells(rc, includeIdentity); @@ -164,7 +164,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 100; var columns = Common.GetColumns(false); var rs = Common.GetResultSet(columns, false); - var etm = Common.GetMetadata(columns); + var etm = Common.GetStandardMetadata(columns); RowCreate rc = new RowCreate(rowId, rs, etm); // If: I attempt to create a command with a null connection @@ -179,7 +179,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 100; var columns = Common.GetColumns(false); var rs = Common.GetResultSet(columns, false); - var etm = Common.GetMetadata(columns); + var etm = Common.GetStandardMetadata(columns); var mockConn = new TestSqlConnection(null); // If: I ask for a script to be generated without setting any values diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs index 321ed582..5308a0a9 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs @@ -23,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create the values to store const long rowId = 100; ResultSet rs = QueryExecution.Common.GetBasicExecutedBatch().ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); // If: I create a RowCreate instance RowCreate rc = new RowCreate(rowId, rs, etm); @@ -41,7 +41,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { DbColumn[] columns = Common.GetColumns(true); ResultSet rs = Common.GetResultSet(columns, true); - IEditTableMetadata etm = Common.GetMetadata(columns, false, isMemoryOptimized); + IEditTableMetadata etm = Common.GetStandardMetadata(columns, false, isMemoryOptimized); // If: I ask for a script to be generated for delete RowDelete rd = new RowDelete(0, rs, etm); @@ -68,7 +68,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 0; var columns = Common.GetColumns(false); var rs = Common.GetResultSet(columns, false); - var etm = Common.GetMetadata(columns); + var etm = Common.GetStandardMetadata(columns); // If: I ask for the change to be applied RowDelete rd = new RowDelete(rowId, rs, etm); @@ -90,7 +90,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData const long rowId = 0; var columns = Common.GetColumns(includeIdentity); var rs = Common.GetResultSet(columns, includeIdentity); - var etm = Common.GetMetadata(columns, !includeIdentity, isMemoryOptimized); + var etm = Common.GetStandardMetadata(columns, !includeIdentity, isMemoryOptimized); RowDelete rd = new RowDelete(rowId, rs, etm); // ... Mock db connection for building the command @@ -133,7 +133,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a row delete var columns = Common.GetColumns(false); var rs = Common.GetResultSet(columns, false); - var etm = Common.GetMetadata(columns); + var etm = Common.GetStandardMetadata(columns); RowDelete rd = new RowDelete(0, rs, etm); // If: I attempt to create a command with a null connection @@ -146,7 +146,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { DbColumn[] columns = Common.GetColumns(true); ResultSet rs = Common.GetResultSet(columns, true); - IEditTableMetadata etm = Common.GetMetadata(columns, false); + IEditTableMetadata etm = Common.GetStandardMetadata(columns, false); // If: I set a cell on a delete row edit // Then: It should throw as invalid operation diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs index 1b98d16c..274f11c2 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs @@ -59,7 +59,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata provider with a single column var cols = new[] {col}; ResultSet rs = GetResultSet(cols, new[] {val}); - IEditTableMetadata etm = Common.GetMetadata(cols); + IEditTableMetadata etm = Common.GetStandardMetadata(cols); RowEditTester rt = new RowEditTester(rs, etm); rt.ValidateWhereClauseSingleKey(nullClause); @@ -82,7 +82,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata provider with multiple key columns DbColumn[] cols = {new TestDbColumn("col1"), new TestDbColumn("col2")}; ResultSet rs = GetResultSet(cols, new object[] {"abc", "def"}); - IEditTableMetadata etm = Common.GetMetadata(cols); + IEditTableMetadata etm = Common.GetStandardMetadata(cols); RowEditTester rt = new RowEditTester(rs, etm); rt.ValidateWhereClauseMultipleKeys(); @@ -94,7 +94,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata provider with no key columns DbColumn[] cols = {new TestDbColumn("col1"), new TestDbColumn("col2")}; ResultSet rs = GetResultSet(cols, new object[] {"abc", "def"}); - IEditTableMetadata etm = Common.GetMetadata(new DbColumn[] {}); + IEditTableMetadata etm = Common.GetStandardMetadata(new DbColumn[] {}); RowEditTester rt = new RowEditTester(rs, etm); rt.ValidateWhereClauseNoKeys(); @@ -106,7 +106,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata we can reuse var cols = Common.GetColumns(false); var rs = Common.GetResultSet(cols, false); - var etm = Common.GetMetadata(cols); + var etm = Common.GetStandardMetadata(cols); // If: I request to sort a list of the three different edit operations List rowEdits = new List @@ -128,7 +128,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata we can reuse var cols = Common.GetColumns(false); var rs = Common.GetResultSet(cols, false, 4); - var etm = Common.GetMetadata(cols); + var etm = Common.GetStandardMetadata(cols); // If: I sort 3 edit operations of the same type List rowEdits = new List @@ -151,7 +151,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata we can reuse var cols = Common.GetColumns(false); var rs = Common.GetResultSet(cols, false); - var etm = Common.GetMetadata(cols); + var etm = Common.GetStandardMetadata(cols); // If: I sort 3 edit operations of the same type List rowEdits = new List @@ -174,7 +174,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a result set and metadata we can reuse var cols = Common.GetColumns(false); var rs = Common.GetResultSet(cols, false); - var etm = Common.GetMetadata(cols); + var etm = Common.GetStandardMetadata(cols); // If: I sort 3 delete operations of the same type List rowEdits = new List diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs index f6abfb3b..bd0fb2d1 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs @@ -23,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create the values to store const long rowId = 0; ResultSet rs = QueryExecution.Common.GetBasicExecutedBatch().ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); // If: I create a RowUpdate instance RowUpdate rc = new RowUpdate(rowId, rs, etm); @@ -40,7 +40,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a fake table to update DbColumn[] columns = Common.GetColumns(true); ResultSet rs = Common.GetResultSet(columns, true); - IEditTableMetadata etm = Common.GetMetadata(columns); + IEditTableMetadata etm = Common.GetStandardMetadata(columns); // If: // ... I add updates to all the cells in the row @@ -77,7 +77,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a fake table to update DbColumn[] columns = Common.GetColumns(true); ResultSet rs = Common.GetResultSet(columns, true); - IEditTableMetadata etm = Common.GetMetadata(columns, false, isMemoryOptimized); + IEditTableMetadata etm = Common.GetStandardMetadata(columns, false, isMemoryOptimized); // If: I ask for a script to be generated for update RowUpdate ru = new RowUpdate(0, rs, etm); @@ -116,7 +116,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a row update with cell updates var columns = Common.GetColumns(includeIdentity); var rs = Common.GetResultSet(columns, includeIdentity); - var etm = Common.GetMetadata(columns, !includeIdentity, isMemoryOptimized); + var etm = Common.GetStandardMetadata(columns, !includeIdentity, isMemoryOptimized); RowUpdate ru = new RowUpdate(0, rs, etm); Common.AddCells(ru, includeIdentity); @@ -169,7 +169,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a row create var columns = Common.GetColumns(false); var rs = Common.GetResultSet(columns, false); - var etm = Common.GetMetadata(columns); + var etm = Common.GetStandardMetadata(columns); RowUpdate rc = new RowUpdate(0, rs, etm); // If: I attempt to create a command with a null connection @@ -186,7 +186,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a row update (no cell updates needed) var columns = Common.GetColumns(includeIdentity); var rs = Common.GetResultSet(columns, includeIdentity); - var etm = Common.GetMetadata(columns, !includeIdentity); + var etm = Common.GetStandardMetadata(columns, !includeIdentity); RowUpdate ru = new RowUpdate(0, rs, etm); long oldBytesWritten = rs.totalBytesWritten; @@ -209,7 +209,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a row update (no cell updates needed) var columns = Common.GetColumns(true); var rs = Common.GetResultSet(columns, true); - var etm = Common.GetMetadata(columns, false); + var etm = Common.GetStandardMetadata(columns, false); RowUpdate ru = new RowUpdate(0, rs, etm); // If: I ask for the changes to be applied with a null db reader diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs index fabfd9ad..fa856e90 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs @@ -6,11 +6,14 @@ using System; using System.Linq; using System.Threading.Tasks; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.EditData; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; using Microsoft.SqlTools.ServiceLayer.QueryExecution; using Microsoft.SqlTools.ServiceLayer.Test.Common; +using Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution; using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; using Moq; using Xunit; @@ -356,7 +359,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); return s; } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs index 4f02f781..fb2092cf 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs @@ -10,8 +10,10 @@ using System.IO; using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.EditData; +using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; using Microsoft.SqlTools.ServiceLayer.QueryExecution; +using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.SqlContext; using Microsoft.SqlTools.ServiceLayer.Test.Common; using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; @@ -29,7 +31,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I create a session object without a null query // Then: It should throw an exception - Assert.Throws(() => new EditSession(null, Common.GetMetadata(new DbColumn[] {}))); + Assert.Throws(() => new EditSession(null, Common.GetStandardMetadata(new DbColumn[] {}))); } [Fact] @@ -48,7 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // If: I create a session object with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // Then: @@ -122,7 +124,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail @@ -147,22 +149,83 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // If: I add a row to the session - long newId = s.CreateRow(); + EditCreateRowResult result = s.CreateRow(); // Then: // ... The new ID should be equal to the row count - Assert.Equal(rs.RowCount, newId); + Assert.Equal(rs.RowCount, result.NewRowId); // ... The next row ID should have been incremented Assert.Equal(rs.RowCount + 1, s.NextRowId); // ... There should be a new row create object in the cache - Assert.Contains(newId, s.EditCache.Keys); - Assert.IsType(s.EditCache[newId]); + Assert.Contains(result.NewRowId, s.EditCache.Keys); + Assert.IsType(s.EditCache[result.NewRowId]); + + // ... The default values should be returned (we will test this in depth below) + Assert.NotEmpty(result.DefaultValues); + } + + [Fact] + public void CreateRowDefaultTest() + { + // Setup: + // ... We will have 3 columns + DbColumn[] cols = + { + new TestDbColumn("col1", false), // No default + new TestDbColumn("col2", false), // Has default (defined below) + new TestDbColumn("filler", false) // Filler column so we can use the common code + }; + + // ... Metadata provider will return 3 columns + EditColumnWrapper[] metas = + { + new EditColumnWrapper // No default + { + DbColumn = new DbColumnWrapper(cols[0]), + DefaultValue = null, + EscapedName = cols[0].ColumnName, + Ordinal = 0, + IsKey = false + }, + new EditColumnWrapper // Has default + { + DbColumn = new DbColumnWrapper(cols[1]), + DefaultValue = "default", + EscapedName = cols[0].ColumnName, + Ordinal = 0, + IsKey = false + }, + new EditColumnWrapper() + }; + var etm = Common.GetMetadataProvider(metas, true); + + // ... Create a result set + var rs = Common.GetResultSet(cols, false, 1); + + // ... Create a session from all this + var session = new EditSession(rs, etm); + + // If: I add a row to the session, on a table that has defaults + var result = session.CreateRow(); + + // Then: + // ... Result should not be null, new row ID should be > 0 + Assert.NotNull(result); + Assert.True(result.NewRowId > 0); + + // ... There should be 3 default values (3 columns) + Assert.NotEmpty(result.DefaultValues); + Assert.Equal(3, result.DefaultValues.Length); + + // ... There should be specific values for each kind of default + Assert.Null(result.DefaultValues[0]); + Assert.Equal("default", result.DefaultValues[1]); } #endregion @@ -174,7 +237,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // If: I delete a row that is out of range for the result set @@ -207,7 +270,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail @@ -229,7 +292,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // If: I add a row to the session @@ -250,7 +313,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // If: I revert a row that doesn't have any pending changes @@ -265,7 +328,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail @@ -291,7 +354,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail @@ -315,7 +378,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // If: I update a cell on a row that does not have a pending edit @@ -340,7 +403,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Setup: Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // If: I try to script the edit cache with a null or whitespace output path @@ -355,7 +418,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... Create a session with a proper query and metadata Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); EditSession s = new EditSession(rs, etm); // ... Add two mock edits that will generate a script @@ -534,7 +597,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns); return new EditSession(rs, etm); } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/SqlScriptFormatterTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/SqlScriptFormatterTests.cs index d220724c..b34ea0a5 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/SqlScriptFormatterTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/SqlScriptFormatterTests.cs @@ -11,7 +11,7 @@ using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.Utility; using Xunit; -namespace Microsoft.SqlTools.ServiceLayer.Test.Utility +namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility { public class SqlScriptFormatterTests { @@ -308,6 +308,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Utility #endregion + [Theory] + [InlineData("(0)", "0")] + [InlineData("((0))", "0")] + [InlineData("('')", "")] + [InlineData("('stuff')", "stuff")] + [InlineData("(N'')", "")] + [InlineData("(N'stuff')", "stuff")] + [InlineData("('''stuff')", "'stuff")] + [InlineData("(N'stu''''ff')", "stu''ff")] + public void UnescapeTest(string input, string output) + { + Assert.Equal(output, SqlScriptFormatter.UnwrapLiteral(input)); + } + private class FormatterTestDbColumn : DbColumn { public FormatterTestDbColumn(string dataType, int? precision = null, int? scale = null)