diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditInitializeRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditInitializeRequest.cs
index ecc1b9f8..e0d9805b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditInitializeRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditInitializeRequest.cs
@@ -31,6 +31,11 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts
/// The type of the object to use for generating an edit script
///
public string ObjectType { get; set; }
+
+ ///
+ /// The query used to retrieve result set
+ ///
+ public string QueryString { get; set; }
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs
index e1a07972..299db9fe 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs
@@ -146,6 +146,62 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
return query.Batches[0].ResultSets[0];
}
+ ///
+ /// If the results contain any results that conflict with the table metadata, then
+ /// make all columns readonly so that the user cannot make an invalid update.
+ ///
+ public static void CheckResultsForInvalidColumns(ResultSet results, string tableName)
+ {
+ if(SchemaContainsMultipleItems(results.Columns, col => col.BaseCatalogName)
+ || SchemaContainsMultipleItems(results.Columns, col => col.BaseSchemaName)
+ || SchemaContainsMultipleItems(results.Columns, col => col.BaseTableName))
+ {
+ throw new InvalidOperationException(SR.EditDataMultiTableNotSupported);
+ }
+
+ // Check if any of the columns are invalid
+ HashSet colNameTracker = new HashSet();
+ foreach (DbColumnWrapper col in results.Columns)
+ {
+ if (col.IsAliased.HasTrue())
+ {
+ throw new InvalidOperationException(SR.EditDataAliasesNotSupported);
+ }
+
+ if (col.IsExpression.HasTrue())
+ {
+ throw new InvalidOperationException(SR.EditDataExpressionsNotSupported);
+ }
+
+ if (colNameTracker.Contains(col.ColumnName))
+ {
+ throw new InvalidOperationException(SR.EditDataDuplicateColumnsNotSupported);
+ }
+ else
+ {
+ colNameTracker.Add(col.ColumnName);
+ }
+ }
+
+ // Only one source table in the metadata, but check if results are from the original table.
+ if (results.Columns.Length > 0)
+ {
+ string resultTableName = results.Columns[0].BaseTableName;
+ if (!string.IsNullOrEmpty(resultTableName) && !string.Equals(resultTableName, tableName))
+ {
+ throw new InvalidOperationException(SR.EditDataIncorrectTable(tableName));
+ }
+ }
+ }
+
+ private static bool SchemaContainsMultipleItems(DbColumn[] columns, Func filter)
+ {
+ return columns
+ .Select(column => filter(column))
+ .Where(name => name != null)
+ .ToHashSet().Count > 1;
+ }
+
///
/// Creates a new row update and adds it to the update cache
///
@@ -415,7 +471,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
initParams.ObjectType);
// Step 2) Get and execute a query for the rows in the object we're looking up
- EditSessionQueryExecutionState state = await queryRunner(ConstructInitializeQuery(objectMetadata, initParams.Filters));
+ EditSessionQueryExecutionState state = await queryRunner(initParams.QueryString ?? ConstructInitializeQuery(objectMetadata, initParams.Filters));
if (state.Query == null)
{
string message = state.Message ?? SR.EditDataQueryFailed;
@@ -424,6 +480,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
// Step 3) Setup the internal state
associatedResultSet = ValidateQueryForSession(state.Query);
+ CheckResultsForInvalidColumns(associatedResultSet, initParams.ObjectName);
+
NextRowId = associatedResultSet.RowCount;
EditCache = new ConcurrentDictionary();
IsInitialized = true;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs
index d765ee2e..d94c64a3 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditTableMetadata.cs
@@ -3,8 +3,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+using System;
+using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
@@ -55,6 +58,40 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
#endregion
+ ///
+ /// Filters out metadata that is not present in the result set, and matches metadata ordering to resultset.
+ ///
+ public static EditColumnMetadata[] FilterColumnMetadata(EditColumnMetadata[] metaColumns, DbColumnWrapper[] resultColumns)
+ {
+ if (metaColumns.Length == 0)
+ {
+ return metaColumns;
+ }
+
+ bool escapeColName = FromSqlScript.IsIdentifierBracketed(metaColumns[0].EscapedName);
+ Dictionary columnNameOrdinalMap = new Dictionary(capacity: resultColumns.Length);
+ for (int i = 0; i < resultColumns.Length; i++)
+ {
+ DbColumnWrapper column = resultColumns[i];
+ string columnName = column.ColumnName;
+ if (escapeColName && !FromSqlScript.IsIdentifierBracketed(columnName))
+ {
+ columnName = ToSqlScript.FormatIdentifier(columnName);
+ }
+ columnNameOrdinalMap.Add(columnName, column.ColumnOrdinal ?? i);
+ }
+
+ HashSet resultColumnNames = columnNameOrdinalMap.Keys.ToHashSet();
+ metaColumns = Array.FindAll(metaColumns, column => resultColumnNames.Contains(column.EscapedName));
+ foreach (EditColumnMetadata metaCol in metaColumns)
+ {
+ metaCol.Ordinal = columnNameOrdinalMap[metaCol.EscapedName];
+ }
+ Array.Sort(metaColumns, (x, y) => (Comparer.Default).Compare(x.Ordinal, y.Ordinal));
+
+ return metaColumns;
+ }
+
///
/// Extracts extended column properties from the database columns from SQL Client
///
@@ -63,6 +100,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
{
Validate.IsNotNull(nameof(dbColumnWrappers), dbColumnWrappers);
+ Columns = EditTableMetadata.FilterColumnMetadata(Columns, dbColumnWrappers);
+
// Iterate over the column wrappers and improve the columns we have
for (int i = 0; i < Columns.Length; i++)
{
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs
index 7b6c6484..a1ebd2d4 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs
@@ -42,10 +42,10 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
public RowCreate(long rowId, ResultSet associatedResultSet, EditTableMetadata associatedMetadata)
: base(rowId, associatedResultSet, associatedMetadata)
{
- newCells = new CellUpdate[associatedResultSet.Columns.Length];
+ newCells = new CellUpdate[AssociatedResultSet.Columns.Length];
// Process the default cell values. If the column is calculated, then the value is a placeholder
- DefaultValues = associatedMetadata.Columns.Select((col, index) => col.IsCalculated.HasTrue()
+ DefaultValues = AssociatedObjectMetadata.Columns.Select((col, index) => col.IsCalculated.HasTrue()
? SR.EditDataComputedColumnPlaceholder
: col.DefaultValue).ToArray();
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEditBase.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEditBase.cs
index 0bdc089a..305ac596 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEditBase.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEditBase.cs
@@ -45,8 +45,10 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
}
RowId = rowId;
- AssociatedResultSet = associatedResultSet;
AssociatedObjectMetadata = associatedMetadata;
+ AssociatedResultSet = associatedResultSet;
+
+ AssociatedObjectMetadata.Columns = EditTableMetadata.FilterColumnMetadata(AssociatedObjectMetadata.Columns, AssociatedResultSet.Columns);
}
#region Properties
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs
index 615e266d..62e22fb4 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs
@@ -44,7 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
: base(rowId, associatedResultSet, associatedMetadata)
{
cellUpdates = new ConcurrentDictionary();
- associatedRow = associatedResultSet.GetRow(rowId);
+ associatedRow = AssociatedResultSet.GetRow(rowId);
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
index 71ca4655..1c14741b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
@@ -236,6 +236,16 @@ EditDataNullNotAllowed = NULL is not allowed for this column
EditDataValueTooLarge(string value, string columnType) = Value {0} is too large to fit in column of type {1}
+EditDataMultiTableNotSupported = EditData queries targeting multiple tables are not supported
+
+EditDataAliasesNotSupported = EditData queries with aliased columns are not supported
+
+EditDataExpressionsNotSupported = EditData queries with aggregate or expression columns are not supported
+
+EditDataDuplicateColumnsNotSupported = EditData queries with duplicate columns are not supported
+
+EditDataIncorrectTable(string tableName) = EditData queries must query the originally targeted table '{0}'
+
############################################################################
# DacFx Resources
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
index 4bf9d74f..99bf919e 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
@@ -16,6 +16,7 @@ using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.Utility;
using System.Globalization;
+using System.Collections.ObjectModel;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
@@ -339,6 +340,21 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
dbCommand.CommandTimeout = 0;
executionStartTime = DateTime.Now;
+ // Fetch schema info separately, since CommandBehavior.KeyInfo will include primary
+ // key columns in the result set, even if they weren't part of the select statement.
+ // Extra key columns get added to the end, so just correlate via Column Ordinal.
+ List columnSchemas = new List();
+ using (DbDataReader reader = await dbCommand.ExecuteReaderAsync(CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly, cancellationToken))
+ {
+ if (reader != null && reader.CanGetColumnSchema())
+ {
+ do
+ {
+ columnSchemas.Add(reader.GetColumnSchema().ToArray());
+ } while (await reader.NextResultAsync(cancellationToken));
+ }
+ }
+
// Execute the command to get back a reader
using (DbDataReader reader = await dbCommand.ExecuteReaderAsync(cancellationToken))
{
@@ -375,6 +391,42 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
await SendMessage(SR.QueryServiceCompletedSuccessfully, false);
}
}
+
+ if (columnSchemas != null)
+ {
+ ExtendResultMetadata(columnSchemas, resultSets);
+ }
+ }
+ }
+
+ private void ExtendResultMetadata(List columnSchemas, List results)
+ {
+ if (columnSchemas.Count != results.Count) return;
+
+ for(int i = 0; i < results.Count; i++)
+ {
+ ResultSet result = results[i];
+ DbColumn[] columnSchema = columnSchemas[i];
+ if(result.Columns.Length > columnSchema.Length)
+ {
+ throw new InvalidOperationException("Did not receive enough metadata columns.");
+ }
+
+ for(int j = 0; j < result.Columns.Length; j++)
+ {
+ DbColumnWrapper resultCol = result.Columns[j];
+ DbColumn schemaCol = columnSchema[j];
+
+ if(!string.Equals(resultCol.DataTypeName, schemaCol.DataTypeName)
+ || (!string.Equals(resultCol.ColumnName, schemaCol.ColumnName)
+ && !string.IsNullOrEmpty(schemaCol.ColumnName)
+ && !string.Equals(resultCol, SR.QueryServiceColumnNull)))
+ {
+ throw new InvalidOperationException("Inconsistent column metadata.");
+ }
+
+ result.Columns[j] = new DbColumnWrapper(schemaCol);
+ }
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatters/FromSqlScript.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatters/FromSqlScript.cs
index 7cad30b6..84c4f70d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatters/FromSqlScript.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatters/FromSqlScript.cs
@@ -18,7 +18,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters
{
// Regex: optionally starts with N, captures string wrapped in single quotes
private static readonly Regex StringRegex = new Regex("^N?'(.*)'$", RegexOptions.Compiled);
-
+ private static readonly Regex BracketRegex = new Regex(@"^\[(.*)\]$", RegexOptions.Compiled);
+
///
/// Decodes a multipart identifier as used in a SQL script into an array of the multiple
/// parts of the identifier. Implemented as a state machine that iterates over the
@@ -128,6 +129,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters
return literal;
}
+ ///
+ /// Tests whether an identifier is escaped with brackets e.g. [Northwind].[dbo].[Orders]
+ ///
+ /// Identifier to check.
+ /// Boolean indicating if identifier is escaped with brackets.
+ public static bool IsIdentifierBracketed(string identifer) => BracketRegex.IsMatch(identifer);
+
#region Private Helpers
private static bool HasNextCharacter(string haystack, char needle, int position)
@@ -143,7 +151,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters
// Replace 2x of the escape character with 1x of the escape character
return value.Replace(new string(escapeCharacter, 2), escapeCharacter.ToString());
}
-
+
#endregion
}
}
\ No newline at end of file
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/FilterMetadataTest.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/FilterMetadataTest.cs
new file mode 100644
index 00000000..4d1610fd
--- /dev/null
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/FilterMetadataTest.cs
@@ -0,0 +1,95 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.ServiceLayer.EditData;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
+using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
+using Xunit;
+
+namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
+{
+ ///
+ /// When using generic SQL queries to retrieve EditData rows, the columns in the result set may be
+ /// a reordered subset of the columns that are present in the complete table metadata.
+ /// EditTableMetadata.FilterColumnMetadata() filters out unnecessary columns from the retrieved
+ /// table metadata, and reorders the metadata columns so that it matches the same column
+ /// ordering in the result set.
+ ///
+ public class FilterMetadataTest
+ {
+ [Fact]
+ public void BasicFilterTest()
+ {
+ EditColumnMetadata[] metas = CreateMetadataColumns(new string[] { "[col1]", "[col2]", "[col3]" });
+ DbColumnWrapper[] cols = CreateColumnWrappers(new string[] { metas[0].EscapedName, metas[1].EscapedName, metas[2].EscapedName });
+
+ EditColumnMetadata[] filteredData = EditTableMetadata.FilterColumnMetadata(metas, cols);
+ ValidateFilteredData(filteredData, cols);
+ }
+
+ [Fact]
+ public void ReorderedResultsTest()
+ {
+ EditColumnMetadata[] metas = CreateMetadataColumns(new string[] { "[col1]", "[col2]", "[col3]" });
+ DbColumnWrapper[] cols = CreateColumnWrappers(new string[] { metas[1].EscapedName, metas[2].EscapedName, metas[0].EscapedName });
+
+ EditColumnMetadata[] filteredData = EditTableMetadata.FilterColumnMetadata(metas, cols);
+ ValidateFilteredData(filteredData, cols);
+ }
+
+ [Fact]
+ public void LessResultColumnsTest()
+ {
+ EditColumnMetadata[] metas = CreateMetadataColumns(new string[] { "[col1]", "[col2]", "[col3]", "[fillerCol1]", "[fillerCol2]" });
+ DbColumnWrapper[] cols = CreateColumnWrappers(new string[] { metas[0].EscapedName, metas[1].EscapedName, metas[2].EscapedName });
+
+ EditColumnMetadata[] filteredData = EditTableMetadata.FilterColumnMetadata(metas, cols);
+ ValidateFilteredData(filteredData, cols);
+ }
+
+ [Fact]
+ public void EmptyDataTest()
+ {
+ EditColumnMetadata[] metas = new EditColumnMetadata[0];
+ DbColumnWrapper[] cols = new DbColumnWrapper[0];
+
+ EditColumnMetadata[] filteredData = EditTableMetadata.FilterColumnMetadata(metas, cols);
+ ValidateFilteredData(filteredData, cols);
+ }
+
+ private DbColumnWrapper[] CreateColumnWrappers(string[] colNames)
+ {
+ DbColumnWrapper[] cols = new DbColumnWrapper[colNames.Length];
+ for (int i = 0; i < cols.Length; i++)
+ {
+ cols[i] = new DbColumnWrapper(new TestDbColumn(colNames[i], i));
+ }
+ return cols;
+ }
+
+ private EditColumnMetadata[] CreateMetadataColumns(string[] colNames)
+ {
+ EditColumnMetadata[] metas = new EditColumnMetadata[colNames.Length];
+ for (int i = 0; i < metas.Length; i++)
+ {
+ metas[i] = new EditColumnMetadata { EscapedName = colNames[i], Ordinal = i };
+ }
+ return metas;
+ }
+
+ private void ValidateFilteredData(EditColumnMetadata[] filteredData, DbColumnWrapper[] cols)
+ {
+ Assert.Equal(cols.Length, filteredData.Length);
+ for (int i = 0; i < cols.Length; i++)
+ {
+ Assert.Equal(cols[i].ColumnName, filteredData[i].EscapedName);
+ if (cols[i].ColumnOrdinal.HasValue)
+ {
+ Assert.Equal(cols[i].ColumnOrdinal, filteredData[i].Ordinal);
+ }
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
index f296ad37..423b6b87 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
@@ -194,14 +194,17 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
new EditColumnMetadata // No default
{
DefaultValue = null,
- EscapedName = cols[0].ColumnName,
+ EscapedName = cols[0].ColumnName
},
new EditColumnMetadata // Has default
{
DefaultValue = "default",
- EscapedName = cols[0].ColumnName,
+ EscapedName = cols[1].ColumnName
},
- new EditColumnMetadata()
+ new EditColumnMetadata
+ {
+ EscapedName = cols[2].ColumnName
+ }
};
var etm = new EditTableMetadata
{
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbColumn.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbColumn.cs
index 4603ebac..812a0ec9 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbColumn.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbColumn.cs
@@ -83,7 +83,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
/// Constructs a basic DbColumn that is an NVARCHAR(128) NULL
///
/// Name of the column
- public TestDbColumn(string columnName)
+ public TestDbColumn(string columnName, int? columnOrdinal = null)
{
base.IsLong = false;
base.ColumnName = columnName;
@@ -91,6 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
base.AllowDBNull = true;
base.DataType = typeof(string);
base.DataTypeName = "nvarchar";
+ base.ColumnOrdinal = columnOrdinal;
}
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/UtilityTests/FromSqlScriptTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/UtilityTests/FromSqlScriptTests.cs
index 919259d6..3e33a346 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/UtilityTests/FromSqlScriptTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/UtilityTests/FromSqlScriptTests.cs
@@ -75,5 +75,21 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.UtilityTests
{
Assert.Equal(output, FromSqlScript.UnwrapLiteral(input));
}
+
+ [Theory]
+ [InlineData("[name]", true)]
+ [InlineData("[ name ]", true)]
+ [InlineData("[na[[]me]", true)]
+ [InlineData("[]", true)]
+ [InlineData("name", false)]
+ [InlineData("[name", false)]
+ [InlineData("name]", false)]
+ [InlineData("[]name", false)]
+ [InlineData("name[]", false)]
+ [InlineData("[na]me", false)]
+ public void BracketedIdentifierTest(string input, bool output)
+ {
+ Assert.Equal(output, FromSqlScript.IsIdentifierBracketed(input));
+ }
}
}
\ No newline at end of file