Adding decoding of multipart identifiers, default schema workaround (#295)

This change adds a couple things

_Multipart Identifier Decoding_
The ability to decode a multipart identifier (with or without escaping) has been added to the SqlScriptFormatter utility class. This code is utilized to split a table name provided to the edit/initialize request into schema and table name.

_Default Schema Workaround_
The code that retrieves the SMO metadata objects originally used the `[]` operator to access the objects. Due to a bug(?) in SMO, this results in problems when loading tables without a default schema (in our case if you're logged in as SA). Using the metadata object constructors gets around this issue, we are explicitly using them.

* Adding decoding of multipart identifiers
Adding code fix for default schema issue

* Adding some more localizable strings for errors when loading metadata

* Adding localization files... again?

* Changes as per pull request comments
This commit is contained in:
Benjamin Russell
2017-03-27 17:14:21 -07:00
committed by GitHub
parent 1909310a92
commit f7036f3f73
11 changed files with 235 additions and 17 deletions

View File

@@ -15,6 +15,7 @@ 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.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
@@ -423,14 +424,14 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
try
{
// Step 1) Look up the SMO metadata
objectMetadata = metadataFactory.GetObjectMetadata(await connector(), initParams.ObjectName,
string[] namedParts = SqlScriptFormatter.DecodeMultipartIdenfitier(initParams.ObjectName);
objectMetadata = metadataFactory.GetObjectMetadata(await connector(), namedParts,
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));
if (state.Query == null)
{
// TODO: Move to SR file
string message = state.Message ?? SR.EditDataQueryFailed;
throw new Exception(message);
}

View File

@@ -16,9 +16,13 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// Generates a edit-ready metadata object
/// </summary>
/// <param name="connection">Connection to use for getting metadata</param>
/// <param name="objectName">Name of the object to return metadata for</param>
/// <param name="objectNamedParts">
/// The multipart name for the object split and unwrapped. At most two components can be
/// provided (schema, table/view name). At minimum table/view name can be provided, and
/// default schema will be used for schema name.
/// </param>
/// <param name="objectType">Type of the object to return metadata for</param>
/// <returns>Metadata about the object requested</returns>
EditTableMetadata GetObjectMetadata(DbConnection connection, string objectName, string objectType);
EditTableMetadata GetObjectMetadata(DbConnection connection, string[] objectNamedParts, string objectType);
}
}

View File

@@ -11,6 +11,7 @@ using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
@@ -23,11 +24,21 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// Generates a edit-ready metadata object using SMO
/// </summary>
/// <param name="connection">Connection to use for getting metadata</param>
/// <param name="objectName">Name of the object to return metadata for</param>
/// <param name="objectNamedParts">Split and unwrapped name parts</param>
/// <param name="objectType">Type of the object to return metadata for</param>
/// <returns>Metadata about the object requested</returns>
public EditTableMetadata GetObjectMetadata(DbConnection connection, string objectName, string objectType)
public EditTableMetadata GetObjectMetadata(DbConnection connection, string[] objectNamedParts, string objectType)
{
Validate.IsNotNull(nameof(objectNamedParts), objectNamedParts);
if (objectNamedParts.Length <= 0)
{
throw new ArgumentNullException(nameof(objectNamedParts), SR.EditDataMetadataObjectNameRequired);
}
if (objectNamedParts.Length > 2)
{
throw new InvalidOperationException(SR.EditDataMetadataTooManyIdentifiers);
}
// Get a connection to the database for SMO purposes
SqlConnection sqlConn = connection as SqlConnection;
if (sqlConn == null)
@@ -46,22 +57,23 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
// Connect with SMO and get the metadata for the table
Server server = new Server(new ServerConnection(sqlConn));
Database db = new Database(server, sqlConn.Database);
TableViewTableTypeBase smoResult;
switch (objectType.ToLowerInvariant())
{
case "table":
smoResult = server.Databases[sqlConn.Database].Tables[objectName];
smoResult = objectNamedParts.Length == 1
? new Table(db, objectNamedParts[0]) // No schema provided
: new Table(db, objectNamedParts[1], objectNamedParts[0]); // Schema provided
break;
case "view":
smoResult = server.Databases[sqlConn.Database].Views[objectName];
smoResult = objectNamedParts.Length == 1
? new View(db, objectNamedParts[0]) // No schema provided
: new View(db, objectNamedParts[1], objectNamedParts[0]); // Schema provided
break;
default:
throw new ArgumentOutOfRangeException(nameof(objectType), SR.EditDataUnsupportedObjectType(objectType));
}
if (smoResult == null)
{
throw new ArgumentOutOfRangeException(nameof(objectName), SR.EditDataObjectMetadataNotFound);
}
// Generate the edit column metadata
List<EditColumnMetadata> editColumns = new List<EditColumnMetadata>();