Remove SELECT * from edit/initialize Query (#288)

* Major refactor of EditDataMetadata providers

* EditMetadataFactory generates "basic" EditTableMetadata objects based entirely on SMO metadata
* SmoEditTableMetadata no longer depends on SMO, making it unecessary to mock it
* Renamed SmoEditTableMetadata to EditTableMetadata
* EditTableMetadata can be extended with DbColumnWrappers

* Moving logic for extending a EditColumnMetadata into that class

* I *think* this will work for async execution of initialize tasks

* Fixing unit tests for new Edit(Table|Column)Metadata classes

* Async stuff that works! And passes unit tests

* Adding unit tests
Adding .idea to gitignore

* Adding message to the EditSessionReadyEvent

* Fixes from dev merge

* Fixing unit tests that Rider didn't catch as failing
May have been a bit heavy-handed with the async/await stuff

* Couple changes as per PR comments
This commit is contained in:
Benjamin Russell
2017-03-22 10:53:24 -07:00
committed by GitHub
parent d7ecfb1a87
commit 16b3874f28
30 changed files with 2325 additions and 1417 deletions

8
.gitignore vendored
View File

@@ -58,6 +58,9 @@ cross/rootfs/
[Bb]uild[Ll]og.*
test*json
## Project Rider ##
.idea/
#NUNIT
*.VisualState.xml
TestResult.xml
@@ -169,9 +172,6 @@ publish/
*.nupkg
**/packages/*
# NuGet package restore lockfiles
project.lock.json
# Windows Azure Build Output
csx/
*.build.csdef
@@ -243,8 +243,6 @@ $RECYCLE.BIN/
### Linux ###
*~
# KDE directory preferences
.directory

View File

@@ -14,6 +14,12 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts
/// </summary>
public string OwnerUri { get; set; }
/// <summary>
/// Message to explain why a session failed. Should only be set when <see cref="Success"/>
/// is <c>false</c>.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Whether or not the session is ready
/// </summary>

View File

@@ -0,0 +1,113 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Data;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
/// <summary>
/// Small class that stores information needed by the edit data service to properly process
/// edits into scripts.
/// </summary>
public class EditColumnMetadata
{
/// <summary>
/// Constructs a simple edit column metadata provider
/// </summary>
public EditColumnMetadata()
{
HasExtendedProperties = false;
}
#region Basic Properties (properties provided by SMO)
/// <summary>
/// If set, this is a string representation of the default value. If set to null, then the
/// column does not have a default value.
/// </summary>
public string DefaultValue { get; set; }
/// <summary>
/// Escaped identifier for the name of the column
/// </summary>
public string EscapedName { get; set; }
/// <summary>
/// Whether or not the column is computed
/// </summary>
public bool IsComputed { get; set; }
/// <summary>
/// Whether or not the column is deterministically computed
/// </summary>
public bool IsDeterministic { get; set; }
/// <summary>
/// Whether or not the column is an identity column
/// </summary>
public bool IsIdentity { get; set; }
/// <summary>
/// The ordinal ID of the column
/// </summary>
public int Ordinal { get; set; }
#endregion
#region Extended Properties (properties provided by SqlClient)
public DbColumnWrapper DbColumn { get; private set; }
/// <summary>
/// Whether or not the column has extended properties
/// </summary>
public bool HasExtendedProperties { get; private set; }
/// <summary>
/// Whether or not the column is calculated on the server side. This could be a computed
/// column or a identity column.
/// </summary>
public bool? IsCalculated { get; private set; }
/// <summary>
/// Whether or not the column is used in a key to uniquely identify a row
/// </summary>
public bool? IsKey { get; private set; }
/// <summary>
/// Whether or not the column can be trusted for uniqueness
/// </summary>
public bool? IsTrustworthyForUniqueness { get; private set; }
#endregion
/// <summary>
/// Extracts extended column properties from the database columns from SQL Client
/// </summary>
/// <param name="dbColumn">The column information provided by SQL Client</param>
public void Extend(DbColumnWrapper dbColumn)
{
Validate.IsNotNull(nameof(dbColumn), dbColumn);
DbColumn = dbColumn;
// A column is trustworthy for uniqueness if it can be updated or it has an identity
// property. If both of these are false (eg, timestamp) we can't trust it to uniquely
// identify a row in the table
IsTrustworthyForUniqueness = dbColumn.IsUpdatable || dbColumn.IsIdentity.HasTrue();
// A key column is determined by whether it is a key
IsKey = dbColumn.IsKey;
// A column is calculated if it is identity, computed, or otherwise not updatable
IsCalculated = IsIdentity || IsComputed || !dbColumn.IsUpdatable;
// Mark the column as extended
HasExtendedProperties = true;
}
}
}

View File

@@ -1,53 +0,0 @@
//
// 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.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
/// <summary>
/// Small class that stores information needed by the edit data service to properly process
/// edits into scripts.
/// </summary>
public class EditColumnWrapper
{
/// <summary>
/// The DB column
/// </summary>
public DbColumnWrapper DbColumn { get; set; }
/// <summary>
/// If set, this is a string representation of the default value. If set to null, then the
/// column does not have a default value.
/// </summary>
public string DefaultValue { get; set; }
/// <summary>
/// Escaped identifier for the name of the column
/// </summary>
public string EscapedName { get; set; }
/// <summary>
/// Whether or not the column is calculated on the server side. This could be a computed
/// column or a identity column.
/// </summary>
public bool IsCalculated { get; set; }
/// <summary>
/// Whether or not the column is used in a key to uniquely identify a row
/// </summary>
public bool IsKey { get; set; }
/// <summary>
/// Whether or not the column can be trusted for uniqueness
/// </summary>
public bool IsTrustworthyForUniqueness { get; set; }
/// <summary>
/// The ordinal ID of the column
/// </summary>
public int Ordinal { get; set; }
}
}

View File

@@ -13,7 +13,6 @@ using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
using ConnectionType = Microsoft.SqlTools.ServiceLayer.Connection.ConnectionType;
@@ -57,10 +56,6 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
private readonly Lazy<ConcurrentDictionary<string, EditSession>> editSessions = new Lazy<ConcurrentDictionary<string, EditSession>>(
() => new ConcurrentDictionary<string, EditSession>());
private readonly Lazy<ConcurrentDictionary<string, TaskCompletionSource<bool>>> initializeWaitHandles =
new Lazy<ConcurrentDictionary<string, TaskCompletionSource<bool>>>(
() => new ConcurrentDictionary<string, TaskCompletionSource<bool>>());
#endregion
#region Properties
@@ -70,12 +65,6 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// </summary>
internal ConcurrentDictionary<string, EditSession> ActiveSessions => editSessions.Value;
/// <summary>
/// Dictionary mapping OwnerURIs to wait handlers for initialize tasks. Pretty much only
/// provided for unit test scenarios.
/// </summary>
internal ConcurrentDictionary<string, TaskCompletionSource<bool>> InitializeWaitHandles => initializeWaitHandles.Value;
#endregion
/// <summary>
@@ -159,63 +148,32 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
internal async Task HandleInitializeRequest(EditInitializeParams initParams,
RequestContext<EditInitializeResult> requestContext)
{
Func<Exception, Task> executionFailureHandler = (e) => SendSessionReadyEvent(requestContext, initParams.OwnerUri, false, e.Message);
Func<Task> executionSuccessHandler = () => SendSessionReadyEvent(requestContext, initParams.OwnerUri, true, null);
EditSession.Connector connector = () => connectionService.GetOrOpenConnection(initParams.OwnerUri, ConnectionType.Edit);
EditSession.QueryRunner queryRunner = q => SessionInitializeQueryRunner(initParams.OwnerUri, requestContext, q);
try
{
{
// Make sure we have info to process this request
Validate.IsNotNullOrWhitespaceString(nameof(initParams.OwnerUri), initParams.OwnerUri);
Validate.IsNotNullOrWhitespaceString(nameof(initParams.ObjectName), initParams.ObjectName);
Validate.IsNotNullOrWhitespaceString(nameof(initParams.ObjectType), initParams.ObjectType);
// Try to add a new wait handler to the
if (!InitializeWaitHandles.TryAdd(initParams.OwnerUri, new TaskCompletionSource<bool>()))
// Create a session and add it to the session list
EditSession session = new EditSession(metadataFactory, initParams.ObjectName, initParams.ObjectType);
if (!ActiveSessions.TryAdd(initParams.OwnerUri, session))
{
throw new InvalidOperationException(SR.EditDataInitializeInProgress);
throw new InvalidOperationException(SR.EditDataSessionAlreadyExists);
}
// Setup a callback for when the query has successfully created
Func<Query, Task<bool>> queryCreateSuccessCallback = async query =>
{
await requestContext.SendResult(new EditInitializeResult());
return true;
};
// Setup a callback for when the query failed to be created
Func<string, Task> queryCreateFailureCallback = async message =>
{
await requestContext.SendError(message);
CompleteInitializeWaitHandler(initParams.OwnerUri, false);
};
// Setup a callback for when the query completes execution successfully
Query.QueryAsyncEventHandler queryCompleteSuccessCallback =
q => QueryCompleteCallback(q, initParams, requestContext);
// Setup a callback for when the query completes execution with failure
Query.QueryAsyncEventHandler queryCompleteFailureCallback = async query =>
{
EditSessionReadyParams readyParams = new EditSessionReadyParams
{
OwnerUri = initParams.OwnerUri,
Success = false
};
await requestContext.SendEvent(EditSessionReadyEvent.Type, readyParams);
CompleteInitializeWaitHandler(initParams.OwnerUri, false);
};
// Put together a query for the results and execute it
ExecuteStringParams executeParams = new ExecuteStringParams
{
Query = $"SELECT * FROM {SqlScriptFormatter.FormatMultipartIdentifier(initParams.ObjectName)}",
OwnerUri = initParams.OwnerUri
};
await queryExecutionService.InterServiceExecuteQuery(executeParams, requestContext,
queryCreateSuccessCallback, queryCreateFailureCallback,
queryCompleteSuccessCallback, queryCompleteFailureCallback);
// Initialize the session
session.Initialize(connector, queryRunner, executionSuccessHandler, executionFailureHandler);
}
catch (Exception e)
{
await requestContext.SendError(e.Message);
CompleteInitializeWaitHandler(initParams.OwnerUri, false);
}
}
@@ -313,52 +271,64 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
return editSession;
}
private async Task QueryCompleteCallback(Query query, EditInitializeParams initParams,
IEventSender requestContext)
private async Task<EditSession.EditSessionQueryExecutionState> SessionInitializeQueryRunner(string ownerUri,
IEventSender eventSender, string query)
{
EditSessionReadyParams readyParams = new EditSessionReadyParams
// Open a task completion source, effectively creating a synchronous block
TaskCompletionSource<EditSession.EditSessionQueryExecutionState> taskCompletion =
new TaskCompletionSource<EditSession.EditSessionQueryExecutionState>();
// Setup callback for successful query creation
// NOTE: We do not want to set the task completion source, since we will continue executing the query after
Func<Query, Task<bool>> queryCreateSuccessCallback = q => Task.FromResult(true);
// Setup callback for failed query creation
Func<string, Task> queryCreateFailureCallback = m =>
{
OwnerUri = initParams.OwnerUri
taskCompletion.SetResult(new EditSession.EditSessionQueryExecutionState(null, m));
return Task.FromResult(0);
};
try
// Setup callback for successful query execution
Query.QueryAsyncEventHandler queryCompleteSuccessCallback = q =>
{
// Validate the query for a editSession
ResultSet resultSet = EditSession.ValidateQueryForSession(query);
taskCompletion.SetResult(new EditSession.EditSessionQueryExecutionState(q));
return Task.FromResult(0);
};
// Get a connection we'll use for SMO metadata lookup (and committing, later on)
DbConnection conn = await connectionService.GetOrOpenConnection(initParams.OwnerUri, ConnectionType.Edit);
var metadata = metadataFactory.GetObjectMetadata(conn, resultSet.Columns,
initParams.ObjectName, initParams.ObjectType);
// Create the editSession and add it to the sessions list
EditSession editSession = new EditSession(resultSet, metadata);
if (!ActiveSessions.TryAdd(initParams.OwnerUri, editSession))
{
throw new InvalidOperationException("Failed to create edit editSession, editSession already exists.");
}
readyParams.Success = true;
}
catch (Exception)
// Setup callback for failed query execution
Query.QueryAsyncEventHandler queryCompleteFailureCallback = q =>
{
// Request that the query be disposed
await queryExecutionService.InterServiceDisposeQuery(initParams.OwnerUri, null, null);
readyParams.Success = false;
}
taskCompletion.SetResult(new EditSession.EditSessionQueryExecutionState(null));
return Task.FromResult(0);
};
// Send the edit session ready notification
await requestContext.SendEvent(EditSessionReadyEvent.Type, readyParams);
CompleteInitializeWaitHandler(initParams.OwnerUri, true);
// Execute the query
ExecuteStringParams executeParams = new ExecuteStringParams
{
Query = query,
OwnerUri = ownerUri
};
await queryExecutionService.InterServiceExecuteQuery(executeParams, eventSender,
queryCreateSuccessCallback, queryCreateFailureCallback,
queryCompleteSuccessCallback, queryCompleteFailureCallback);
// Wait for the completion source to complete, this will wait until the query has
// completed and sent all its events.
return await taskCompletion.Task;
}
private void CompleteInitializeWaitHandler(string ownerUri, bool result)
private static Task SendSessionReadyEvent(IEventSender eventSender, string ownerUri, bool success,
string message)
{
// If there isn't a wait handler, just ignore it
TaskCompletionSource<bool> initializeWaiter;
if (ownerUri != null && InitializeWaitHandles.TryRemove(ownerUri, out initializeWaiter))
var sessionReadyParams = new EditSessionReadyParams
{
initializeWaiter.SetResult(result);
}
OwnerUri = ownerUri,
Message = message,
Success = success
};
return eventSender.SendEvent(EditSessionReadyEvent.Type, sessionReadyParams);
}
#endregion

View File

@@ -25,28 +25,37 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
public class EditSession
{
private readonly ResultSet associatedResultSet;
private readonly IEditTableMetadata objectMetadata;
private ResultSet associatedResultSet;
private readonly IEditMetadataFactory metadataFactory;
private EditTableMetadata objectMetadata;
private readonly string objectName;
private readonly string objectType;
/// <summary>
/// Constructs a new edit session bound to the result set and metadat object provided
/// </summary>
/// <param name="resultSet">The result set of the table to be edited</param>
/// <param name="objMetadata">Metadata provider for the table to be edited</param>
public EditSession(ResultSet resultSet, IEditTableMetadata objMetadata)
/// <param name="metaFactory">Factory for creating metadata</param>
/// <param name="objName">The name of the object to edit</param>
/// <param name="objType">The type of the object to edit</param>
public EditSession(IEditMetadataFactory metaFactory, string objName, string objType)
{
Validate.IsNotNull(nameof(resultSet), resultSet);
Validate.IsNotNull(nameof(objMetadata), objMetadata);
Validate.IsNotNull(nameof(metaFactory), metaFactory);
Validate.IsNotNullOrWhitespaceString(nameof(objName), objName);
Validate.IsNotNullOrWhitespaceString(nameof(objType), objType);
// Setup the internal state
associatedResultSet = resultSet;
objectMetadata = objMetadata;
NextRowId = associatedResultSet.RowCount;
EditCache = new ConcurrentDictionary<long, RowEditBase>();
metadataFactory = metaFactory;
objectName = objName;
objectType = objType;
}
#region Properties
public delegate Task<DbConnection> Connector();
public delegate Task<EditSessionQueryExecutionState> QueryRunner(string query);
/// <summary>
/// The task that is running to commit the changes to the db
/// Internal for unit test purposes.
@@ -61,12 +70,43 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <summary>
/// The cache of pending updates. Internal for unit test purposes only
/// </summary>
internal ConcurrentDictionary<long, RowEditBase> EditCache { get; }
internal ConcurrentDictionary<long, RowEditBase> EditCache { get; private set; }
/// <summary>
/// The task that is running to initialize the edit session
/// </summary>
internal Task InitializeTask { get; set; }
/// <summary>
/// Whether or not the session has been initialized
/// </summary>
public bool IsInitialized { get; internal set; }
#endregion
#region Public Methods
public void Initialize(Connector connector, QueryRunner queryRunner, Func<Task> successHandler, Func<Exception, Task> errorHandler)
{
if (IsInitialized)
{
throw new InvalidOperationException(SR.EditDataSessionAlreadyInitialized);
}
if (InitializeTask != null)
{
throw new InvalidOperationException(SR.EditDataSessionAlreadyInitializing);
}
Validate.IsNotNull(nameof(connector), connector);
Validate.IsNotNull(nameof(queryRunner), queryRunner);
Validate.IsNotNull(nameof(successHandler), successHandler);
Validate.IsNotNull(nameof(errorHandler), errorHandler);
// Start up the initialize process
InitializeTask = InitializeInternal(connector, queryRunner, successHandler, errorHandler);
}
/// <summary>
/// Validates that a query can be used for an edit session. The target result set is returned
/// </summary>
@@ -100,6 +140,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <returns>The internal ID of the newly created row</returns>
public EditCreateRowResult CreateRow()
{
ThrowIfNotInitialized();
// Create a new row ID (atomically, since this could be accesses concurrently)
long newRowId = NextRowId++;
@@ -113,13 +155,13 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
}
// 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++)
string[] defaultValues = new string[objectMetadata.Columns.Length];
for(int i = 0; i < objectMetadata.Columns.Length; i++)
{
EditColumnWrapper col = objectMetadata.Columns[i];
EditColumnMetadata col = objectMetadata.Columns[i];
// If the column is calculated, return the calculated placeholder as the display value
if (col.IsCalculated)
if (col.IsCalculated.HasTrue())
{
defaultValues[i] = SR.EditDataComputedColumnPlaceholder;
}
@@ -150,6 +192,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <param name="errorHandler">Callback to perform if the commit process has failed at some point</param>
public void CommitEdits(DbConnection connection, Func<Task> successHandler, Func<Exception, Task> errorHandler)
{
ThrowIfNotInitialized();
Validate.IsNotNull(nameof(connection), connection);
Validate.IsNotNull(nameof(successHandler), successHandler);
Validate.IsNotNull(nameof(errorHandler), errorHandler);
@@ -173,6 +217,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <param name="rowId">The internal ID of the row to delete</param>
public void DeleteRow(long rowId)
{
ThrowIfNotInitialized();
// Sanity check the row ID
if (rowId >= NextRowId || rowId < 0)
{
@@ -196,6 +242,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <returns>An array of rows with pending edits applied</returns>
public async Task<EditRow[]> GetRows(long startIndex, int rowCount)
{
ThrowIfNotInitialized();
// Get the cached rows from the result set
ResultSetSubset cachedRows = startIndex < associatedResultSet.RowCount
? await associatedResultSet.GetSubset(startIndex, rowCount)
@@ -249,6 +297,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <returns>String version of the old value for the cell</returns>
public string RevertCell(long rowId, int columnId)
{
ThrowIfNotInitialized();
// Attempt to get the row edit with the given ID
RowEditBase pendingEdit;
if (!EditCache.TryGetValue(rowId, out pendingEdit))
@@ -269,6 +319,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <param name="rowId">The internal ID of the row to reset</param>
public void RevertRow(long rowId)
{
ThrowIfNotInitialized();
// Attempt to remove the row with the given ID
RowEditBase removedEdit;
if (!EditCache.TryRemove(rowId, out removedEdit))
@@ -284,6 +336,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <returns></returns>
public string ScriptEdits(string outputPath)
{
ThrowIfNotInitialized();
// Validate the output path
// @TODO: Reinstate this code once we have an interface around file generation
//if (outputPath == null)
@@ -328,6 +382,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// <param name="newValue">The new string value of the cell to update</param>
public EditUpdateCellResult UpdateCell(long rowId, int columnId, string newValue)
{
ThrowIfNotInitialized();
// Sanity check to make sure that the row ID is in the range of possible values
if (rowId >= NextRowId || rowId < 0)
{
@@ -347,6 +403,38 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
#endregion
private async Task InitializeInternal(Connector connector, QueryRunner queryRunner,
Func<Task> successHandler, Func<Exception, Task> failureHandler)
{
try
{
// Step 1) Look up the SMO metadata
objectMetadata = metadataFactory.GetObjectMetadata(await connector(), objectName, objectType);
// Step 2) Get and execute a query for the rows in the object we're looking up
EditSessionQueryExecutionState state = await queryRunner(ConstructInitializeQuery());
if (state.Query == null)
{
// TODO: Move to SR file
string message = state.Message ?? SR.EditDataQueryFailed;
throw new Exception(message);
}
// Step 3) Setup the internal state
associatedResultSet = ValidateQueryForSession(state.Query);
NextRowId = associatedResultSet.RowCount;
EditCache = new ConcurrentDictionary<long, RowEditBase>();
IsInitialized = true;
// Step 4) Return our success
await successHandler();
}
catch (Exception e)
{
await failureHandler(e);
}
}
private async Task CommitEditsInternal(DbConnection connection, Func<Task> successHandler, Func<Exception, Task> errorHandler)
{
try
@@ -378,5 +466,50 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
await errorHandler(e);
}
}
private string ConstructInitializeQuery()
{
// Using the columns we know, put together a query for the rows in the table
var columns = objectMetadata.Columns.Select(col => col.EscapedName);
var columnClause = string.Join(", ", columns);
return $"SELECT ${columnClause} FROM ${objectMetadata.EscapedMultipartName}";
}
private void ThrowIfNotInitialized()
{
if (!IsInitialized)
{
throw new InvalidOperationException(SR.EditDataSessionNotInitialized);
}
}
/// <summary>
/// State object to return upon completion of an edit session intialization query
/// </summary>
public class EditSessionQueryExecutionState
{
/// <summary>
/// The query object that was used to execute the edit initialization query. If
/// <c>null</c> the query was not successfully executed.
/// </summary>
public Query Query { get; set; }
/// <summary>
/// Any message that may have occurred during execution of the query (ie, exceptions).
/// If this is and <see cref="Query"/> are <c>null</c> then the error messages were
/// returned via message events.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Constructs a new instance. Sets the values of the properties.
/// </summary>
public EditSessionQueryExecutionState(Query query, string message = null)
{
Query = query;
Message = message;
}
}
}
}

View File

@@ -0,0 +1,85 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
/// <summary>
/// Provides metadata about the table or view being edited
/// </summary>
public class EditTableMetadata
{
/// <summary>
/// Constructs a simple edit table metadata provider
/// </summary>
public EditTableMetadata()
{
HasExtendedProperties = false;
}
#region Basic Properties (properties provided by SMO)
/// <summary>
/// List of columns in the object being edited
/// </summary>
public EditColumnMetadata[] Columns { get; set; }
/// <summary>
/// Full escaped multipart identifier for the object being edited
/// </summary>
public string EscapedMultipartName { get; set; }
/// <summary>
/// Whether or not the object being edited is memory optimized
/// </summary>
public bool IsMemoryOptimized { get; set; }
#endregion
#region Extended Properties (properties provided by SqlClient)
/// <summary>
/// Whether or not the table has had extended properties added to it
/// </summary>
public bool HasExtendedProperties { get; private set; }
/// <summary>
/// List of columns that are used to uniquely identify a row
/// </summary>
public EditColumnMetadata[] KeyColumns { get; private set; }
#endregion
/// <summary>
/// Extracts extended column properties from the database columns from SQL Client
/// </summary>
/// <param name="dbColumnWrappers">The column information provided by SQL Client</param>
public void Extend(DbColumnWrapper[] dbColumnWrappers)
{
Validate.IsNotNull(nameof(dbColumnWrappers), dbColumnWrappers);
// Iterate over the column wrappers and improve the columns we have
for (int i = 0; i < Columns.Length; i++)
{
Columns[i].Extend(dbColumnWrappers[i]);
}
// Determine what the key columns are
KeyColumns = Columns.Where(c => c.IsKey.HasTrue()).ToArray();
if (KeyColumns.Length == 0)
{
// We didn't find any explicit key columns. Instead, we'll use all columns that are
// trustworthy for uniqueness (usually all the columns)
KeyColumns = Columns.Where(c => c.IsTrustworthyForUniqueness.HasTrue()).ToArray();
}
// Mark that the table is now extended
HasExtendedProperties = true;
}
}
}

View File

@@ -4,7 +4,6 @@
//
using System.Data.Common;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
@@ -17,10 +16,9 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
/// Generates a edit-ready metadata object
/// </summary>
/// <param name="connection">Connection to use for getting metadata</param>
/// <param name="columns">List of columns from a query against the object</param>
/// <param name="objectName">Name of the object to return metadata for</param>
/// <param name="objectType">Type of the object to return metadata for</param>
/// <returns>Metadata about the object requested</returns>
IEditTableMetadata GetObjectMetadata(DbConnection connection, DbColumnWrapper[] columns, string objectName, string objectType);
EditTableMetadata GetObjectMetadata(DbConnection connection, string objectName, string objectType);
}
}

View File

@@ -1,36 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
/// <summary>
/// An interface used in edit scenarios that defines properties for what columns are primary
/// keys, and other metadata of the table.
/// </summary>
public interface IEditTableMetadata
{
/// <summary>
/// All columns in the table that's being edited
/// </summary>
IReadOnlyList<EditColumnWrapper> Columns { get; }
/// <summary>
/// The escaped name of the table that's being edited
/// </summary>
string EscapedMultipartName { get; }
/// <summary>
/// Whether or not this table is a memory optimized table
/// </summary>
bool IsMemoryOptimized { get; }
/// <summary>
/// Columns that can be used to uniquely identify the a row
/// </summary>
IReadOnlyList<EditColumnWrapper> KeyColumns { get; }
}
}

View File

@@ -4,12 +4,13 @@
//
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
@@ -22,11 +23,10 @@ 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="columns">List of columns from a query against the object</param>
/// <param name="objectName">Name of the object to return metadata for</param>
/// <param name="objectType">Type of the object to return metadata for</param>
/// <returns>Metadata about the object requested</returns>
public IEditTableMetadata GetObjectMetadata(DbConnection connection, DbColumnWrapper[] columns, string objectName, string objectType)
public EditTableMetadata GetObjectMetadata(DbConnection connection, string objectName, string objectType)
{
// Get a connection to the database for SMO purposes
SqlConnection sqlConn = connection as SqlConnection;
@@ -44,25 +44,59 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
sqlConn = reliableConn.GetUnderlyingConnection();
}
// Connect with SMO and get the metadata for the table
Server server = new Server(new ServerConnection(sqlConn));
TableViewTableTypeBase result;
TableViewTableTypeBase smoResult;
switch (objectType.ToLowerInvariant())
{
case "table":
result = server.Databases[sqlConn.Database].Tables[objectName];
smoResult = server.Databases[sqlConn.Database].Tables[objectName];
break;
case "view":
result = server.Databases[sqlConn.Database].Views[objectName];
smoResult = server.Databases[sqlConn.Database].Views[objectName];
break;
default:
throw new ArgumentOutOfRangeException(nameof(objectType), SR.EditDataUnsupportedObjectType(objectType));
}
if (result == null)
if (smoResult == null)
{
throw new ArgumentOutOfRangeException(nameof(objectName), SR.EditDataObjectMetadataNotFound);
}
return new SmoEditTableMetadata(columns, result);
// Generate the edit column metadata
List<EditColumnMetadata> editColumns = new List<EditColumnMetadata>();
for (int i = 0; i < smoResult.Columns.Count; i++)
{
Column smoColumn = smoResult.Columns[i];
// The default value may be escaped
string defaultValue = smoColumn.DefaultConstraint == null
? null
: SqlScriptFormatter.UnwrapLiteral(smoColumn.DefaultConstraint.Text);
EditColumnMetadata column = new EditColumnMetadata
{
DefaultValue = defaultValue,
EscapedName = SqlScriptFormatter.FormatIdentifier(smoColumn.Name),
Ordinal = i,
};
editColumns.Add(column);
}
// Only tables can be memory-optimized
Table smoTable = smoResult as Table;
bool isMemoryOptimized = smoTable != null && smoTable.IsMemoryOptimized;
// Escape the parts of the name
string[] objectNameParts = {smoResult.Schema, smoResult.Name};
string escapedMultipartName = SqlScriptFormatter.FormatMultipartIdentifier(objectNameParts);
return new EditTableMetadata
{
Columns = editColumns.ToArray(),
EscapedMultipartName = escapedMultipartName,
IsMemoryOptimized = isMemoryOptimized,
};
}
}
}

View File

@@ -1,109 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
{
/// <summary>
/// Provides metadata about the table or view being edited
/// </summary>
public class SmoEditTableMetadata : IEditTableMetadata
{
private readonly List<EditColumnWrapper> columns;
private readonly List<EditColumnWrapper> keyColumns;
/// <summary>
/// Constructor that extracts useful metadata from the provided metadata objects
/// </summary>
/// <param name="dbColumns">DB columns from the ResultSet</param>
/// <param name="smoObject">SMO metadata object for the table/view being edited</param>
public SmoEditTableMetadata(IList<DbColumnWrapper> dbColumns, TableViewTableTypeBase smoObject)
{
Validate.IsNotNull(nameof(dbColumns), dbColumns);
Validate.IsNotNull(nameof(smoObject), smoObject);
// Make sure that we have equal columns on both metadata providers
Debug.Assert(dbColumns.Count == smoObject.Columns.Count);
// Create the columns for edit usage
columns = new List<EditColumnWrapper>();
for (int i = 0; i < dbColumns.Count; i++)
{
Column smoColumn = smoObject.Columns[i];
DbColumnWrapper dbColumn = dbColumns[i];
// A column is trustworthy for uniqueness if it can be updated or it has an identity
// property. If both of these are false (eg, timestamp) we can't trust it to uniquely
// 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,
// A column is calculated if it is identity, computed, or otherwise not updatable
IsCalculated = smoColumn.Identity || smoColumn.Computed || !dbColumn.IsUpdatable
};
columns.Add(column);
}
// Determine what the key columns are
keyColumns = columns.Where(c => c.IsKey).ToList();
if (keyColumns.Count == 0)
{
// We didn't find any explicit key columns. Instead, we'll use all columns that are
// trustworthy for uniqueness (usually all the columns)
keyColumns = columns.Where(c => c.IsTrustworthyForUniqueness).ToList();
}
// If a table is memory optimized it is Hekaton. If it's a view, then it can't be Hekaton
Table smoTable = smoObject as Table;
IsMemoryOptimized = smoTable != null && smoTable.IsMemoryOptimized;
// Escape the parts of the name
string[] objectNameParts = {smoObject.Schema, smoObject.Name};
EscapedMultipartName = SqlScriptFormatter.FormatMultipartIdentifier(objectNameParts);
}
/// <summary>
/// Read-only list of columns in the object being edited
/// </summary>
public IReadOnlyList<EditColumnWrapper> Columns => columns.AsReadOnly();
/// <summary>
/// Full escaped multipart identifier for the object being edited
/// </summary>
public string EscapedMultipartName { get; }
/// <summary>
/// Whether or not the object being edited is memory optimized
/// </summary>
public bool IsMemoryOptimized { get; }
/// <summary>
/// Read-only list of columns that are used to uniquely identify a row
/// </summary>
public IReadOnlyList<EditColumnWrapper> KeyColumns => keyColumns.AsReadOnly();
}
}

View File

@@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
/// <param name="rowId">Internal ID of the row that is being created</param>
/// <param name="associatedResultSet">The result set for the rows in the table we're editing</param>
/// <param name="associatedMetadata">The metadata for table we're editing</param>
public RowCreate(long rowId, ResultSet associatedResultSet, IEditTableMetadata associatedMetadata)
public RowCreate(long rowId, ResultSet associatedResultSet, EditTableMetadata associatedMetadata)
: base(rowId, associatedResultSet, associatedMetadata)
{
newCells = new CellUpdate[associatedResultSet.Columns.Length];

View File

@@ -28,7 +28,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
/// <param name="rowId">Internal ID of the row to be deleted</param>
/// <param name="associatedResultSet">Result set that is being edited</param>
/// <param name="associatedMetadata">Improved metadata of the object being edited</param>
public RowDelete(long rowId, ResultSet associatedResultSet, IEditTableMetadata associatedMetadata)
public RowDelete(long rowId, ResultSet associatedResultSet, EditTableMetadata associatedMetadata)
: base(rowId, associatedResultSet, associatedMetadata)
{
}

View File

@@ -36,7 +36,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
/// <param name="rowId">The internal ID of the row that is being edited</param>
/// <param name="associatedResultSet">The result set that will be updated</param>
/// <param name="associatedMetadata">Metadata provider for the object to edit</param>
protected RowEditBase(long rowId, ResultSet associatedResultSet, IEditTableMetadata associatedMetadata)
protected RowEditBase(long rowId, ResultSet associatedResultSet, EditTableMetadata associatedMetadata)
{
RowId = rowId;
AssociatedResultSet = associatedResultSet;
@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
/// <summary>
/// The metadata for the table this edit is associated to
/// </summary>
public IEditTableMetadata AssociatedObjectMetadata { get; }
public EditTableMetadata AssociatedObjectMetadata { get; }
/// <summary>
/// Sort ID for a row edit. Ensures that when a collection of RowEditBase objects are
@@ -162,7 +162,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
}
IList<DbCellValue> row = AssociatedResultSet.GetRow(RowId);
foreach (EditColumnWrapper col in AssociatedObjectMetadata.KeyColumns)
foreach (EditColumnMetadata col in AssociatedObjectMetadata.KeyColumns)
{
// Put together a clause for the value of the cell
DbCellValue cellData = row[col.Ordinal];

View File

@@ -39,7 +39,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
/// <param name="rowId">Internal ID of the row that will be updated with this object</param>
/// <param name="associatedResultSet">Result set for the rows of the object to update</param>
/// <param name="associatedMetadata">Metadata provider for the object to update</param>
public RowUpdate(long rowId, ResultSet associatedResultSet, IEditTableMetadata associatedMetadata)
public RowUpdate(long rowId, ResultSet associatedResultSet, EditTableMetadata associatedMetadata)
: base(rowId, associatedResultSet, associatedMetadata)
{
cellUpdates = new ConcurrentDictionary<int, CellUpdate>();

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
@@ -27,106 +105,322 @@
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader><resheader name="version"><value>1.3</value></resheader><resheader name="reader"><value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader><resheader name="writer"><value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader><data name="ConnectionServiceConnectErrorNullParams"><value>Los parámetros de conexión no pueden ser nulos</value></data>
<data name="ConnectionServiceListDbErrorNullOwnerUri"><value>OwnerUri no puede ser nulo ni estar vacío</value></data>
<data name="ConnectionServiceListDbErrorNotConnected"><value>SpecifiedUri '{0}' no tiene una conexión existente</value></data>
<data name="ConnectionServiceConnStringInvalidAuthType"><value>El valor '{0}' no es válido para AuthenticationType. Los valores válidos son 'Integrated' y 'SqlLogin'.</value></data>
<data name="ConnectionServiceConnStringInvalidIntent"><value>El valor '{0}' no es válido para ApplicationIntent. Los valores válidos son 'ReadWrite' y 'ReadOnly'.</value></data>
<data name="ConnectionServiceConnectionCanceled"><value>Conexión cancelada</value></data>
<data name="ConnectionParamsValidateNullOwnerUri"><value>OwnerUri no puede ser nulo ni estar vacío</value></data>
<data name="ConnectionParamsValidateNullConnection"><value>El objeto de detalles de conexión no puede ser nulo</value></data>
<data name="ConnectionParamsValidateNullServerName"><value>ServerName no puede ser nulo ni estar vacío</value></data>
<data name="ConnectionParamsValidateNullSqlAuth"><value>{0} no puede ser nulo ni estar vacío cuando se utiliza autenticación SqlLogin</value></data>
<data name="QueryServiceCancelAlreadyCompleted"><value>Ya se ha completado la consulta, no se puede cancelar</value></data>
<data name="QueryServiceCancelDisposeFailed"><value>La consulta fue cancelada con éxito, pero no se ha podido desechar. No se encontró el URI del propietario.</value></data>
<data name="QueryServiceQueryCancelled"><value>Consulta cancelada por el usuario</value></data>
<data name="QueryServiceSubsetBatchNotCompleted"><value>El lote aún no ha finalizado,</value></data>
<data name="QueryServiceSubsetBatchOutOfRange"><value>Índice de lote no puede ser menor que 0 o mayor que el número de lotes</value></data>
<data name="QueryServiceSubsetResultSetOutOfRange"><value>Índice del conjunto de resultados no puede ser menor que 0 o mayor que el número de conjuntos de resultados</value></data>
<data name="QueryServiceDataReaderByteCountInvalid"><value>El número máximo de bytes a devolver debe ser mayor que cero</value></data>
<data name="QueryServiceDataReaderCharCountInvalid"><value>El número máximo de caracteres a devolver debe ser mayor que cero</value></data>
<data name="QueryServiceDataReaderXmlCountInvalid"><value>El número máximo de bytes XML a devolver debe ser mayor que cero</value></data>
<data name="QueryServiceFileWrapperWriteOnly"><value>El método de acceso no puede ser de sólo escritura</value></data>
<data name="QueryServiceFileWrapperNotInitialized"><value>FileStreamWrapper debe inicializarse antes de realizar operaciones</value></data>
<data name="QueryServiceFileWrapperReadOnly"><value>Este FileStreamWrapper no se puede utilizar para escritura.</value></data>
<data name="QueryServiceAffectedOneRow"><value>(1 fila afectada)</value></data>
<data name="QueryServiceAffectedRows"><value>({0} filas afectadas)</value></data>
<data name="QueryServiceCompletedSuccessfully"><value>Comandos finalizados correctamente.</value></data>
<data name="QueryServiceErrorFormat"><value>Msg {0}, nivel {1} estado {2}, línea {3} {4} {5}</value></data>
<data name="QueryServiceQueryFailed"><value>Error en la consulta: {0}</value></data>
<data name="QueryServiceColumnNull"><value>(Ningún nombre de columna)</value></data>
<data name="QueryServiceRequestsNoQuery"><value>La consulta solicitada no existe</value></data>
<data name="QueryServiceQueryInvalidOwnerUri"><value>Este editor no está conectado a una base de datos</value></data>
<data name="QueryServiceQueryInProgress"><value>Una consulta ya está en curso para esta sesión de editor. Por favor, cancelar esta consulta o esperar su finalización.</value></data>
<data name="QueryServiceMessageSenderNotSql"><value>Remitente de eventos de OnInfoMessage debe ser un objeto SqlConnection</value></data>
<data name="QueryServiceResultSetReaderNull"><value>Lector no puede ser nulo</value></data>
<data name="QueryServiceSaveAsResultSetNotComplete"><value>No se puede guardar el resultado hasta que haya finalizado la ejecución de la consulta</value></data>
<data name="QueryServiceSaveAsMiscStartingError"><value>Error interno al iniciar el guardado de la tarea</value></data>
<data name="QueryServiceSaveAsInProgress"><value>Una operacion de guardado en la misma ruta se encuentra en curso</value></data>
<data name="QueryServiceSaveAsFail"><value>Error al guardar {0}: {1}</value></data>
<data name="QueryServiceResultSetNotRead"><value>No se puede leer el subconjunto, a menos que los resultados se han leído desde el servidor</value></data>
<data name="QueryServiceResultSetStartRowOutOfRange"><value>Fila de inicio no puede ser menor que 0 o mayor que el número de filas en el conjunto de resultados</value></data>
<data name="QueryServiceResultSetRowCountOutOfRange"><value>La cantidad de filas debe ser un entero positivo</value></data>
<data name="QueryServiceResultSetNoColumnSchema"><value>No se pudo recuperar el esquema de columna para el conjunto de resultados</value></data>
<data name="QueryServiceExecutionPlanNotFound"><value>No se pudo recuperar un plan de ejecución del conjunto de resultados</value></data>
<data name="PeekDefinitionAzureError"><value>Esta característica actualmente no se admite en la base de datos de SQL Azure y almacén de datos: {0}</value></data>
<data name="PeekDefinitionError"><value>Se ha producido un error inesperado durante la ejecución de la definición de Peek: {0}</value></data>
<data name="PeekDefinitionNoResultsError"><value>No se encontraron resultados.</value></data>
<data name="PeekDefinitionDatabaseError"><value>No se pudo obtener ningún objeto asociado a la base de datos.</value></data>
<data name="PeekDefinitionNotConnectedError"><value>Conéctese a un servidor.</value></data>
<data name="PeekDefinitionTimedoutError"><value>Tiempo de espera agotado para esta operación.</value></data>
<data name="PeekDefinitionTypeNotSupportedError"><value>Esta característica no admite actualmente este tipo de objeto.</value></data>
<data name="WorkspaceServicePositionLineOutOfRange"><value>Posición está fuera del intervalo de la línea de archivo</value></data>
<data name="WorkspaceServicePositionColumnOutOfRange"><value>Posición está fuera del intervalo de la columna de la línea {0}</value></data>
<data name="WorkspaceServiceBufferPositionOutOfOrder"><value>Posición de inicio ({0}, {1}) debe preceder o ser igual a la posición final ({2}, {3})</value></data>
<data name="EE_BatchSqlMessageNoProcedureInfo"><value>Msg {0}, {1}, nivel de estado {2}, línea {3}</value></data>
<data name="EE_BatchSqlMessageWithProcedureInfo"><value>Msj {0}, {1}, nivel de estado {2}, procedimiento {3}, línea {4}</value></data>
<data name="EE_BatchSqlMessageNoLineInfo"><value>Msg {0}, nivel {1}, {2} de estado</value></data>
<data name="EE_BatchError_Exception"><value>Se produjo un error al procesar el lote. Mensaje de error: {0}</value></data>
<data name="EE_BatchExecutionInfo_RowsAffected"><value>({0} filas afectadas)</value></data>
<data name="EE_ExecutionNotYetCompleteError"><value>La ejecución anterior aún no está completa.</value></data>
<data name="EE_ScriptError_Error"><value>Se ha producido un error de secuencias de comandos.</value></data>
<data name="EE_ScriptError_ParsingSyntax"><value>Se encontró sintaxis incorrecta mientras se estaba analizando {0}.</value></data>
<data name="EE_ScriptError_FatalError"><value>Se ha producido un error grave.</value></data>
<data name="EE_ExecutionInfo_FinalizingLoop"><value>La ejecución completó {0} veces...</value></data>
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>Se canceló la consulta.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>Se produjo un error mientras se ejecutaba el lote.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>Se produjo un error mientras se ejecutaba el lote, pero se ha omitido el error.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>Iniciando bucle de ejecución de {0} veces...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>No se admite el comando {0}.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>La variable {0} no se encontró.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Error de ejecución de SQL: {0}</value></data>
<data name="BatchParserWrapperExecutionError"><value>Ejecución de contenedor del analizador por lotes: {0} se encuentra... en la línea {1}: {2} Descripción: {3}</value></data>
<data name="BatchParserWrapperExecutionEngineBatchMessage"><value>Lote analizador contenedor ejecución motor lote mensaje recibido: mensaje: {0} mensaje detallado: {1}</value></data>
<data name="BatchParserWrapperExecutionEngineBatchResultSetProcessing"><value>Motor de ejecución de analizador contenedor lote ResultSet procesamiento por lotes: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}</value></data>
<data name="BatchParserWrapperExecutionEngineBatchResultSetFinished"><value>Finalizó el elemento ResultSet analizador contenedor ejecución motor los lotes.</value></data>
<data name="BatchParserWrapperExecutionEngineBatchCancelling"><value>Cancelando la ejecución por lotes del contenedor del analizador por lotes.</value></data>
<data name="EE_ScriptError_Warning"><value>Advertencia de scripting.</value></data>
<data name="TroubleshootingAssistanceMessage"><value>Para obtener más información acerca de este error, vea los temas de solución de problemas en la documentación del producto.</value></data>
<data name="BatchParser_CircularReference"><value>El archivo '{0}' se incluyó recursivamente.</value></data>
<data name="BatchParser_CommentNotTerminated"><value>Falta la marca de final de comentario ' * /'.</value></data>
<data name="BatchParser_StringNotTerminated"><value>Sin comilla de cierre después de la cadena de caracteres.</value></data>
<data name="BatchParser_IncorrectSyntax"><value>Se encontró sintaxis incorrecta al analizar '{0}'.</value></data>
<data name="BatchParser_VariableNotDefined"><value>La variable {0} no está definida.</value></data>
<data name="TestLocalizationConstant"><value>prueba</value></data>
<data name="ErrorUnexpectedCodeObjectType"><value>No se puede convertir el SqlCodeObject del Tipo {0} al Tipo {1}</value></data>
<data name="ErrorEmptyStringReplacement"><value>Sustitución de una cadena vacía por una cadena vacía.</value></data>
<data name="EditDataSessionNotFound"><value>Sesión de edición no existe,</value></data>
<data name="EditDataQueryNotCompleted"><value>La consulta no ha finalizado.</value></data>
<data name="EditDataQueryImproperResultSets"><value>La consulta no generó un único set de resultados</value></data>
<data name="EditDataFailedAddRow"><value>Falló al agregar una nueva fila a la caché de actualización</value></data>
<data name="EditDataRowOutOfRange"><value>El ID de la fila ingresado, se encuentra fuera del rango de filas de la caché de edición</value></data>
<data name="EditDataUpdatePending"><value>Una actualización está pendiente para esta fila y debe de revertirse primero</value></data>
<data name="EditDataUpdateNotPending"><value>El ID de la fila ingresado no tiene actualizaciones pendientes</value></data>
<data name="EditDataObjectMetadataNotFound"><value>La metadata de la tabla o vista no pudo ser encontrada</value></data>
<data name="EditDataInvalidFormatBinary"><value>Formato inválido para columna binaria</value></data>
<data name="EditDataInvalidFormatBoolean"><value>Columnas del tipo boolean deben de ser numéricos 1 o 0, o tipo string true o false</value></data>
<data name="EditDataCreateScriptMissingValue"><value>Falta un valor requerido de la celda</value></data>
<data name="EditDataDeleteSetCell"><value>Existe una eliminación pendiente para esta fila, una actualización de celda no puede ser realizada.</value></data>
<data name="EditDataColumnIdOutOfRange"><value>El ID de la columna debe de estar en el rango de columnas de la consulta.</value></data>
<data name="EditDataColumnCannotBeEdited"><value>La columna no puede ser editada</value></data>
<data name="EditDataColumnNoKeyColumns"><value>No se encontró ninguna columna clave</value></data>
<data name="EditDataScriptFilePathNull"><value>Proporcione un nombre de archivo de salida</value></data>
<data name="EditDataUnsupportedObjectType"><value>Objeto de base de datos {0} no puede ser usado para modificación.</value></data>
<data name="ConnectionServiceDbErrorDefaultNotConnected"><value>SpecifiedUri '{0}' no tiene alguna conexión por defecto</value></data>
</root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ConnectionServiceConnectErrorNullParams" xml:space="preserve">
<value>Los parámetros de conexión no pueden ser nulos</value>
</data>
<data name="ConnectionServiceListDbErrorNullOwnerUri" xml:space="preserve">
<value>OwnerUri no puede ser nulo ni estar vacío</value>
</data>
<data name="ConnectionServiceListDbErrorNotConnected" xml:space="preserve">
<value>SpecifiedUri '{0}' no tiene una conexión existente</value>
</data>
<data name="ConnectionServiceConnStringInvalidAuthType" xml:space="preserve">
<value>El valor '{0}' no es válido para AuthenticationType. Los valores válidos son 'Integrated' y 'SqlLogin'.</value>
</data>
<data name="ConnectionServiceConnStringInvalidIntent" xml:space="preserve">
<value>El valor '{0}' no es válido para ApplicationIntent. Los valores válidos son 'ReadWrite' y 'ReadOnly'.</value>
</data>
<data name="ConnectionServiceConnectionCanceled" xml:space="preserve">
<value>Conexión cancelada</value>
</data>
<data name="ConnectionParamsValidateNullOwnerUri" xml:space="preserve">
<value>OwnerUri no puede ser nulo ni estar vacío</value>
</data>
<data name="ConnectionParamsValidateNullConnection" xml:space="preserve">
<value>El objeto de detalles de conexión no puede ser nulo</value>
</data>
<data name="ConnectionParamsValidateNullServerName" xml:space="preserve">
<value>ServerName no puede ser nulo ni estar vacío</value>
</data>
<data name="ConnectionParamsValidateNullSqlAuth" xml:space="preserve">
<value>{0} no puede ser nulo ni estar vacío cuando se utiliza autenticación SqlLogin</value>
</data>
<data name="QueryServiceCancelAlreadyCompleted" xml:space="preserve">
<value>Ya se ha completado la consulta, no se puede cancelar</value>
</data>
<data name="QueryServiceCancelDisposeFailed" xml:space="preserve">
<value>La consulta fue cancelada con éxito, pero no se ha podido desechar. No se encontró el URI del propietario.</value>
</data>
<data name="QueryServiceQueryCancelled" xml:space="preserve">
<value>Consulta cancelada por el usuario</value>
</data>
<data name="QueryServiceSubsetBatchNotCompleted" xml:space="preserve">
<value>El lote aún no ha finalizado,</value>
</data>
<data name="QueryServiceSubsetBatchOutOfRange" xml:space="preserve">
<value>Índice de lote no puede ser menor que 0 o mayor que el número de lotes</value>
</data>
<data name="QueryServiceSubsetResultSetOutOfRange" xml:space="preserve">
<value>Índice del conjunto de resultados no puede ser menor que 0 o mayor que el número de conjuntos de resultados</value>
</data>
<data name="QueryServiceDataReaderByteCountInvalid" xml:space="preserve">
<value>El número máximo de bytes a devolver debe ser mayor que cero</value>
</data>
<data name="QueryServiceDataReaderCharCountInvalid" xml:space="preserve">
<value>El número máximo de caracteres a devolver debe ser mayor que cero</value>
</data>
<data name="QueryServiceDataReaderXmlCountInvalid" xml:space="preserve">
<value>El número máximo de bytes XML a devolver debe ser mayor que cero</value>
</data>
<data name="QueryServiceFileWrapperWriteOnly" xml:space="preserve">
<value>El método de acceso no puede ser de sólo escritura</value>
</data>
<data name="QueryServiceFileWrapperNotInitialized" xml:space="preserve">
<value>FileStreamWrapper debe inicializarse antes de realizar operaciones</value>
</data>
<data name="QueryServiceFileWrapperReadOnly" xml:space="preserve">
<value>Este FileStreamWrapper no se puede utilizar para escritura.</value>
</data>
<data name="QueryServiceAffectedOneRow" xml:space="preserve">
<value>(1 fila afectada)</value>
</data>
<data name="QueryServiceAffectedRows" xml:space="preserve">
<value>({0} filas afectadas)</value>
</data>
<data name="QueryServiceCompletedSuccessfully" xml:space="preserve">
<value>Comandos finalizados correctamente.</value>
</data>
<data name="QueryServiceErrorFormat" xml:space="preserve">
<value>Msg {0}, nivel {1} estado {2}, línea {3} {4} {5}</value>
</data>
<data name="QueryServiceQueryFailed" xml:space="preserve">
<value>Error en la consulta: {0}</value>
</data>
<data name="QueryServiceColumnNull" xml:space="preserve">
<value>(Ningún nombre de columna)</value>
</data>
<data name="QueryServiceRequestsNoQuery" xml:space="preserve">
<value>La consulta solicitada no existe</value>
</data>
<data name="QueryServiceQueryInvalidOwnerUri" xml:space="preserve">
<value>Este editor no está conectado a una base de datos</value>
</data>
<data name="QueryServiceQueryInProgress" xml:space="preserve">
<value>Una consulta ya está en curso para esta sesión de editor. Por favor, cancelar esta consulta o esperar su finalización.</value>
</data>
<data name="QueryServiceMessageSenderNotSql" xml:space="preserve">
<value>Remitente de eventos de OnInfoMessage debe ser un objeto SqlConnection</value>
</data>
<data name="QueryServiceResultSetReaderNull" xml:space="preserve">
<value>Lector no puede ser nulo</value>
</data>
<data name="QueryServiceSaveAsResultSetNotComplete" xml:space="preserve">
<value>No se puede guardar el resultado hasta que haya finalizado la ejecución de la consulta</value>
</data>
<data name="QueryServiceSaveAsMiscStartingError" xml:space="preserve">
<value>Error interno al iniciar el guardado de la tarea</value>
</data>
<data name="QueryServiceSaveAsInProgress" xml:space="preserve">
<value>Una operacion de guardado en la misma ruta se encuentra en curso</value>
</data>
<data name="QueryServiceSaveAsFail" xml:space="preserve">
<value>Error al guardar {0}: {1}</value>
</data>
<data name="QueryServiceResultSetNotRead" xml:space="preserve">
<value>No se puede leer el subconjunto, a menos que los resultados se han leído desde el servidor</value>
</data>
<data name="QueryServiceResultSetStartRowOutOfRange" xml:space="preserve">
<value>Fila de inicio no puede ser menor que 0 o mayor que el número de filas en el conjunto de resultados</value>
</data>
<data name="QueryServiceResultSetRowCountOutOfRange" xml:space="preserve">
<value>La cantidad de filas debe ser un entero positivo</value>
</data>
<data name="QueryServiceResultSetNoColumnSchema" xml:space="preserve">
<value>No se pudo recuperar el esquema de columna para el conjunto de resultados</value>
</data>
<data name="QueryServiceExecutionPlanNotFound" xml:space="preserve">
<value>No se pudo recuperar un plan de ejecución del conjunto de resultados</value>
</data>
<data name="PeekDefinitionAzureError" xml:space="preserve">
<value>Esta característica actualmente no se admite en la base de datos de SQL Azure y almacén de datos: {0}</value>
</data>
<data name="PeekDefinitionError" xml:space="preserve">
<value>Se ha producido un error inesperado durante la ejecución de la definición de Peek: {0}</value>
</data>
<data name="PeekDefinitionNoResultsError" xml:space="preserve">
<value>No se encontraron resultados.</value>
</data>
<data name="PeekDefinitionDatabaseError" xml:space="preserve">
<value>No se pudo obtener ningún objeto asociado a la base de datos.</value>
</data>
<data name="PeekDefinitionNotConnectedError" xml:space="preserve">
<value>Conéctese a un servidor.</value>
</data>
<data name="PeekDefinitionTimedoutError" xml:space="preserve">
<value>Tiempo de espera agotado para esta operación.</value>
</data>
<data name="PeekDefinitionTypeNotSupportedError" xml:space="preserve">
<value>Esta característica no admite actualmente este tipo de objeto.</value>
</data>
<data name="WorkspaceServicePositionLineOutOfRange" xml:space="preserve">
<value>Posición está fuera del intervalo de la línea de archivo</value>
</data>
<data name="WorkspaceServicePositionColumnOutOfRange" xml:space="preserve">
<value>Posición está fuera del intervalo de la columna de la línea {0}</value>
</data>
<data name="WorkspaceServiceBufferPositionOutOfOrder" xml:space="preserve">
<value>Posición de inicio ({0}, {1}) debe preceder o ser igual a la posición final ({2}, {3})</value>
</data>
<data name="EE_BatchSqlMessageNoProcedureInfo" xml:space="preserve">
<value>Msg {0}, {1}, nivel de estado {2}, línea {3}</value>
</data>
<data name="EE_BatchSqlMessageWithProcedureInfo" xml:space="preserve">
<value>Msj {0}, {1}, nivel de estado {2}, procedimiento {3}, línea {4}</value>
</data>
<data name="EE_BatchSqlMessageNoLineInfo" xml:space="preserve">
<value>Msg {0}, nivel {1}, {2} de estado</value>
</data>
<data name="EE_BatchError_Exception" xml:space="preserve">
<value>Se produjo un error al procesar el lote. Mensaje de error: {0}</value>
</data>
<data name="EE_BatchExecutionInfo_RowsAffected" xml:space="preserve">
<value>({0} filas afectadas)</value>
</data>
<data name="EE_ExecutionNotYetCompleteError" xml:space="preserve">
<value>La ejecución anterior aún no está completa.</value>
</data>
<data name="EE_ScriptError_Error" xml:space="preserve">
<value>Se ha producido un error de secuencias de comandos.</value>
</data>
<data name="EE_ScriptError_ParsingSyntax" xml:space="preserve">
<value>Se encontró sintaxis incorrecta mientras se estaba analizando {0}.</value>
</data>
<data name="EE_ScriptError_FatalError" xml:space="preserve">
<value>Se ha producido un error grave.</value>
</data>
<data name="EE_ExecutionInfo_FinalizingLoop" xml:space="preserve">
<value>La ejecución completó {0} veces...</value>
</data>
<data name="EE_ExecutionInfo_QueryCancelledbyUser" xml:space="preserve">
<value>Se canceló la consulta.</value>
</data>
<data name="EE_BatchExecutionError_Halting" xml:space="preserve">
<value>Se produjo un error mientras se ejecutaba el lote.</value>
</data>
<data name="EE_BatchExecutionError_Ignoring" xml:space="preserve">
<value>Se produjo un error mientras se ejecutaba el lote, pero se ha omitido el error.</value>
</data>
<data name="EE_ExecutionInfo_InitilizingLoop" xml:space="preserve">
<value>Iniciando bucle de ejecución de {0} veces...</value>
</data>
<data name="EE_ExecutionError_CommandNotSupported" xml:space="preserve">
<value>No se admite el comando {0}.</value>
</data>
<data name="EE_ExecutionError_VariableNotFound" xml:space="preserve">
<value>La variable {0} no se encontró.</value>
</data>
<data name="BatchParserWrapperExecutionEngineError" xml:space="preserve">
<value>Error de ejecución de SQL: {0}</value>
</data>
<data name="BatchParserWrapperExecutionError" xml:space="preserve">
<value>Ejecución de contenedor del analizador por lotes: {0} se encuentra... en la línea {1}: {2} Descripción: {3}</value>
</data>
<data name="BatchParserWrapperExecutionEngineBatchMessage" xml:space="preserve">
<value>Lote analizador contenedor ejecución motor lote mensaje recibido: mensaje: {0} mensaje detallado: {1}</value>
</data>
<data name="BatchParserWrapperExecutionEngineBatchResultSetProcessing" xml:space="preserve">
<value>Motor de ejecución de analizador contenedor lote ResultSet procesamiento por lotes: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}</value>
</data>
<data name="BatchParserWrapperExecutionEngineBatchResultSetFinished" xml:space="preserve">
<value>Finalizó el elemento ResultSet analizador contenedor ejecución motor los lotes.</value>
</data>
<data name="BatchParserWrapperExecutionEngineBatchCancelling" xml:space="preserve">
<value>Cancelando la ejecución por lotes del contenedor del analizador por lotes.</value>
</data>
<data name="EE_ScriptError_Warning" xml:space="preserve">
<value>Advertencia de scripting.</value>
</data>
<data name="TroubleshootingAssistanceMessage" xml:space="preserve">
<value>Para obtener más información acerca de este error, vea los temas de solución de problemas en la documentación del producto.</value>
</data>
<data name="BatchParser_CircularReference" xml:space="preserve">
<value>El archivo '{0}' se incluyó recursivamente.</value>
</data>
<data name="BatchParser_CommentNotTerminated" xml:space="preserve">
<value>Falta la marca de final de comentario ' * /'.</value>
</data>
<data name="BatchParser_StringNotTerminated" xml:space="preserve">
<value>Sin comilla de cierre después de la cadena de caracteres.</value>
</data>
<data name="BatchParser_IncorrectSyntax" xml:space="preserve">
<value>Se encontró sintaxis incorrecta al analizar '{0}'.</value>
</data>
<data name="BatchParser_VariableNotDefined" xml:space="preserve">
<value>La variable {0} no está definida.</value>
</data>
<data name="TestLocalizationConstant" xml:space="preserve">
<value>prueba</value>
</data>
<data name="ErrorUnexpectedCodeObjectType" xml:space="preserve">
<value>No se puede convertir el SqlCodeObject del Tipo {0} al Tipo {1}</value>
</data>
<data name="ErrorEmptyStringReplacement" xml:space="preserve">
<value>Sustitución de una cadena vacía por una cadena vacía.</value>
</data>
<data name="EditDataSessionNotFound" xml:space="preserve">
<value>Sesión de edición no existe,</value>
</data>
<data name="EditDataQueryNotCompleted" xml:space="preserve">
<value>La consulta no ha finalizado.</value>
</data>
<data name="EditDataQueryImproperResultSets" xml:space="preserve">
<value>La consulta no generó un único set de resultados</value>
</data>
<data name="EditDataFailedAddRow" xml:space="preserve">
<value>Falló al agregar una nueva fila a la caché de actualización</value>
</data>
<data name="EditDataRowOutOfRange" xml:space="preserve">
<value>El ID de la fila ingresado, se encuentra fuera del rango de filas de la caché de edición</value>
</data>
<data name="EditDataUpdatePending" xml:space="preserve">
<value>Una actualización está pendiente para esta fila y debe de revertirse primero</value>
</data>
<data name="EditDataUpdateNotPending" xml:space="preserve">
<value>El ID de la fila ingresado no tiene actualizaciones pendientes</value>
</data>
<data name="EditDataObjectMetadataNotFound" xml:space="preserve">
<value>La metadata de la tabla o vista no pudo ser encontrada</value>
</data>
<data name="EditDataInvalidFormatBinary" xml:space="preserve">
<value>Formato inválido para columna binaria</value>
</data>
<data name="EditDataInvalidFormatBoolean" xml:space="preserve">
<value>Columnas del tipo boolean deben de ser numéricos 1 o 0, o tipo string true o false</value>
</data>
<data name="EditDataCreateScriptMissingValue" xml:space="preserve">
<value>Falta un valor requerido de la celda</value>
</data>
<data name="EditDataDeleteSetCell" xml:space="preserve">
<value>Existe una eliminación pendiente para esta fila, una actualización de celda no puede ser realizada.</value>
</data>
<data name="EditDataColumnIdOutOfRange" xml:space="preserve">
<value>El ID de la columna debe de estar en el rango de columnas de la consulta.</value>
</data>
<data name="EditDataColumnCannotBeEdited" xml:space="preserve">
<value>La columna no puede ser editada</value>
</data>
<data name="EditDataColumnNoKeyColumns" xml:space="preserve">
<value>No se encontró ninguna columna clave</value>
</data>
<data name="EditDataScriptFilePathNull" xml:space="preserve">
<value>Proporcione un nombre de archivo de salida</value>
</data>
<data name="EditDataUnsupportedObjectType" xml:space="preserve">
<value>Objeto de base de datos {0} no puede ser usado para modificación.</value>
</data>
<data name="ConnectionServiceDbErrorDefaultNotConnected" xml:space="preserve">
<value>SpecifiedUri '{0}' no tiene alguna conexión por defecto</value>
</data>
</root>

View File

@@ -120,451 +120,475 @@
<data name="ConnectionServiceConnectErrorNullParams" xml:space="preserve">
<value>Connection parameters cannot be null</value>
<comment></comment>
</data>
</data>
<data name="ConnectionServiceListDbErrorNullOwnerUri" xml:space="preserve">
<value>OwnerUri cannot be null or empty</value>
<comment></comment>
</data>
</data>
<data name="ConnectionServiceListDbErrorNotConnected" xml:space="preserve">
<value>SpecifiedUri '{0}' does not have existing connection</value>
<comment>.
Parameters: 0 - uri (string) </comment>
</data>
</data>
<data name="ConnectionServiceDbErrorDefaultNotConnected" xml:space="preserve">
<value>Specified URI '{0}' does not have a default connection</value>
<comment>.
Parameters: 0 - uri (string) </comment>
</data>
</data>
<data name="ConnectionServiceConnStringInvalidAuthType" xml:space="preserve">
<value>Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'.</value>
<comment>.
Parameters: 0 - authType (string) </comment>
</data>
</data>
<data name="ConnectionServiceConnStringInvalidIntent" xml:space="preserve">
<value>Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.</value>
<comment>.
Parameters: 0 - intent (string) </comment>
</data>
</data>
<data name="ConnectionServiceConnectionCanceled" xml:space="preserve">
<value>Connection canceled</value>
<comment></comment>
</data>
</data>
<data name="ConnectionParamsValidateNullOwnerUri" xml:space="preserve">
<value>OwnerUri cannot be null or empty</value>
<comment></comment>
</data>
</data>
<data name="ConnectionParamsValidateNullConnection" xml:space="preserve">
<value>Connection details object cannot be null</value>
<comment></comment>
</data>
</data>
<data name="ConnectionParamsValidateNullServerName" xml:space="preserve">
<value>ServerName cannot be null or empty</value>
<comment></comment>
</data>
</data>
<data name="ConnectionParamsValidateNullSqlAuth" xml:space="preserve">
<value>{0} cannot be null or empty when using SqlLogin authentication</value>
<comment>.
Parameters: 0 - component (string) </comment>
</data>
</data>
<data name="ErrorUnexpectedCodeObjectType" xml:space="preserve">
<value>Cannot convert SqlCodeObject Type {0} to Type {1}</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceCancelAlreadyCompleted" xml:space="preserve">
<value>The query has already completed, it cannot be cancelled</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceCancelDisposeFailed" xml:space="preserve">
<value>Query successfully cancelled, failed to dispose query. Owner URI not found.</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceQueryCancelled" xml:space="preserve">
<value>Query was canceled by user</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSubsetBatchNotCompleted" xml:space="preserve">
<value>The batch has not completed, yet</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSubsetBatchOutOfRange" xml:space="preserve">
<value>Batch index cannot be less than 0 or greater than the number of batches</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSubsetResultSetOutOfRange" xml:space="preserve">
<value>Result set index cannot be less than 0 or greater than the number of result sets</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceDataReaderByteCountInvalid" xml:space="preserve">
<value>Maximum number of bytes to return must be greater than zero</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceDataReaderCharCountInvalid" xml:space="preserve">
<value>Maximum number of chars to return must be greater than zero</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceDataReaderXmlCountInvalid" xml:space="preserve">
<value>Maximum number of XML bytes to return must be greater than zero</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceFileWrapperWriteOnly" xml:space="preserve">
<value>Access method cannot be write-only</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceFileWrapperNotInitialized" xml:space="preserve">
<value>FileStreamWrapper must be initialized before performing operations</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceFileWrapperReadOnly" xml:space="preserve">
<value>This FileStreamWrapper cannot be used for writing</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceAffectedOneRow" xml:space="preserve">
<value>(1 row affected)</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceAffectedRows" xml:space="preserve">
<value>({0} rows affected)</value>
<comment>.
Parameters: 0 - rows (long) </comment>
</data>
</data>
<data name="QueryServiceCompletedSuccessfully" xml:space="preserve">
<value>Commands completed successfully.</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceErrorFormat" xml:space="preserve">
<value>Msg {0}, Level {1}, State {2}, Line {3}{4}{5}</value>
<comment>.
Parameters: 0 - msg (int), 1 - lvl (int), 2 - state (int), 3 - line (int), 4 - newLine (string), 5 - message (string) </comment>
</data>
</data>
<data name="QueryServiceQueryFailed" xml:space="preserve">
<value>Query failed: {0}</value>
<comment>.
Parameters: 0 - message (string) </comment>
</data>
</data>
<data name="QueryServiceColumnNull" xml:space="preserve">
<value>(No column name)</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceRequestsNoQuery" xml:space="preserve">
<value>The requested query does not exist</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceQueryInvalidOwnerUri" xml:space="preserve">
<value>This editor is not connected to a database</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceQueryInProgress" xml:space="preserve">
<value>A query is already in progress for this editor session. Please cancel this query or wait for its completion.</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceMessageSenderNotSql" xml:space="preserve">
<value>Sender for OnInfoMessage event must be a SqlConnection</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceResultSetAddNoRows" xml:space="preserve">
<value>Cannot add row to result buffer, data reader does not contain rows</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSaveAsResultSetNotComplete" xml:space="preserve">
<value>Result cannot be saved until query execution has completed</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSaveAsMiscStartingError" xml:space="preserve">
<value>Internal error occurred while starting save task</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSaveAsInProgress" xml:space="preserve">
<value>A save request to the same path is in progress</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceSaveAsFail" xml:space="preserve">
<value>Failed to save {0}: {1}</value>
<comment>.
Parameters: 0 - fileName (string), 1 - message (string) </comment>
</data>
</data>
<data name="QueryServiceResultSetNotRead" xml:space="preserve">
<value>Cannot read subset unless the results have been read from the server</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceResultSetStartRowOutOfRange" xml:space="preserve">
<value>Start row cannot be less than 0 or greater than the number of rows in the result set</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceResultSetRowCountOutOfRange" xml:space="preserve">
<value>Row count must be a positive integer</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceResultSetNoColumnSchema" xml:space="preserve">
<value>Could not retrieve column schema for result set</value>
<comment></comment>
</data>
</data>
<data name="QueryServiceExecutionPlanNotFound" xml:space="preserve">
<value>Could not retrieve an execution plan from the result set </value>
<comment></comment>
</data>
</data>
<data name="PeekDefinitionAzureError" xml:space="preserve">
<value>This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}</value>
<comment>.
Parameters: 0 - errorMessage (string) </comment>
</data>
</data>
<data name="PeekDefinitionError" xml:space="preserve">
<value>An unexpected error occurred during Peek Definition execution: {0}</value>
<comment>.
Parameters: 0 - errorMessage (string) </comment>
</data>
</data>
<data name="PeekDefinitionNoResultsError" xml:space="preserve">
<value>No results were found.</value>
<comment></comment>
</data>
</data>
<data name="PeekDefinitionDatabaseError" xml:space="preserve">
<value>No database object was retrieved.</value>
<comment></comment>
</data>
</data>
<data name="PeekDefinitionNotConnectedError" xml:space="preserve">
<value>Please connect to a server.</value>
<comment></comment>
</data>
</data>
<data name="PeekDefinitionTimedoutError" xml:space="preserve">
<value>Operation timed out.</value>
<comment></comment>
</data>
</data>
<data name="PeekDefinitionTypeNotSupportedError" xml:space="preserve">
<value>This object type is currently not supported by this feature.</value>
<comment></comment>
</data>
</data>
<data name="ErrorEmptyStringReplacement" xml:space="preserve">
<value>Replacement of an empty string by an empty string.</value>
<comment></comment>
</data>
</data>
<data name="WorkspaceServicePositionLineOutOfRange" xml:space="preserve">
<value>Position is outside of file line range</value>
<comment></comment>
</data>
</data>
<data name="WorkspaceServicePositionColumnOutOfRange" xml:space="preserve">
<value>Position is outside of column range for line {0}</value>
<comment>.
Parameters: 0 - line (int) </comment>
</data>
</data>
<data name="WorkspaceServiceBufferPositionOutOfOrder" xml:space="preserve">
<value>Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3})</value>
<comment>.
Parameters: 0 - sLine (int), 1 - sCol (int), 2 - eLine (int), 3 - eCol (int) </comment>
</data>
</data>
<data name="EditDataObjectNotFound" xml:space="preserve">
<value>Table or view requested for edit could not be found</value>
<comment></comment>
</data>
<data name="EditDataSessionNotFound" xml:space="preserve">
<value>Edit session does not exist.</value>
<comment></comment>
</data>
</data>
<data name="EditDataSessionAlreadyExists" xml:space="preserve">
<value>Edit session already exists.</value>
<comment></comment>
</data>
<data name="EditDataSessionNotInitialized" xml:space="preserve">
<value>Edit session has not been initialized</value>
<comment></comment>
</data>
<data name="EditDataSessionAlreadyInitialized" xml:space="preserve">
<value>Edit session has already been initialized</value>
<comment></comment>
</data>
<data name="EditDataSessionAlreadyInitializing" xml:space="preserve">
<value>Edit session has already been initialized or is in the process of initializing</value>
<comment></comment>
</data>
<data name="EditDataUnsupportedObjectType" xml:space="preserve">
<value>Database object {0} cannot be used for editing.</value>
<comment>.
Parameters: 0 - typeName (string) </comment>
</data>
</data>
<data name="EditDataQueryFailed" xml:space="preserve">
<value>Query execution failed, see messages for details</value>
<comment></comment>
</data>
<data name="EditDataQueryNotCompleted" xml:space="preserve">
<value>Query has not completed execution</value>
<comment></comment>
</data>
</data>
<data name="EditDataQueryImproperResultSets" xml:space="preserve">
<value>Query did not generate exactly one result set</value>
<comment></comment>
</data>
</data>
<data name="EditDataFailedAddRow" xml:space="preserve">
<value>Failed to add new row to update cache</value>
<comment></comment>
</data>
</data>
<data name="EditDataRowOutOfRange" xml:space="preserve">
<value>Given row ID is outside the range of rows in the edit cache</value>
<comment></comment>
</data>
</data>
<data name="EditDataUpdatePending" xml:space="preserve">
<value>An update is already pending for this row and must be reverted first</value>
<comment></comment>
</data>
</data>
<data name="EditDataUpdateNotPending" xml:space="preserve">
<value>Given row ID does not have pending update</value>
<comment></comment>
</data>
</data>
<data name="EditDataColumnUpdateNotPending" xml:space="preserve">
<value>Give column ID does not have pending update</value>
<comment></comment>
</data>
</data>
<data name="EditDataObjectMetadataNotFound" xml:space="preserve">
<value>Table or view metadata could not be found</value>
<comment></comment>
</data>
</data>
<data name="EditDataInvalidFormatBinary" xml:space="preserve">
<value>Invalid format for binary column</value>
<comment></comment>
</data>
</data>
<data name="EditDataInvalidFormatBoolean" xml:space="preserve">
<value>Allowed values for boolean columns are 0, 1, "true", or "false"</value>
<comment></comment>
</data>
</data>
<data name="EditDataCreateScriptMissingValue" xml:space="preserve">
<value>A required cell value is missing</value>
<comment></comment>
</data>
</data>
<data name="EditDataDeleteSetCell" xml:space="preserve">
<value>A delete is pending for this row, a cell update cannot be applied.</value>
<comment></comment>
</data>
</data>
<data name="EditDataColumnIdOutOfRange" xml:space="preserve">
<value>Column ID must be in the range of columns for the query</value>
<comment></comment>
</data>
</data>
<data name="EditDataColumnCannotBeEdited" xml:space="preserve">
<value>Column cannot be edited</value>
<comment></comment>
</data>
</data>
<data name="EditDataColumnNoKeyColumns" xml:space="preserve">
<value>No key columns were found</value>
<comment></comment>
</data>
</data>
<data name="EditDataScriptFilePathNull" xml:space="preserve">
<value>An output filename must be provided</value>
<comment></comment>
</data>
</data>
<data name="EditDataCommitInProgress" xml:space="preserve">
<value>A commit task is in progress. Please wait for completion.</value>
<comment></comment>
</data>
</data>
<data name="EditDataComputedColumnPlaceholder" xml:space="preserve">
<value>&lt;TBD&gt;</value>
<comment></comment>
</data>
</data>
<data name="EditDataInitializeInProgress" xml:space="preserve">
<value>Another edit data initialize is in progress for this owner URI. Please wait for completion.</value>
<comment></comment>
</data>
</data>
<data name="EditDataTimeOver24Hrs" xml:space="preserve">
<value>TIME column values must be between 00:00:00.0000000 and 23:59:59.9999999</value>
<comment></comment>
</data>
</data>
<data name="EditDataNullNotAllowed" xml:space="preserve">
<value>NULL is not allowed for this column</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchSqlMessageNoProcedureInfo" xml:space="preserve">
<value>Msg {0}, Level {1}, State {2}, Line {3}</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchSqlMessageWithProcedureInfo" xml:space="preserve">
<value>Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchSqlMessageNoLineInfo" xml:space="preserve">
<value>Msg {0}, Level {1}, State {2}</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchError_Exception" xml:space="preserve">
<value>An error occurred while the batch was being processed. The error message is: {0}</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchExecutionInfo_RowsAffected" xml:space="preserve">
<value>({0} row(s) affected)</value>
<comment></comment>
</data>
</data>
<data name="EE_ExecutionNotYetCompleteError" xml:space="preserve">
<value>The previous execution is not yet complete.</value>
<comment></comment>
</data>
</data>
<data name="EE_ScriptError_Error" xml:space="preserve">
<value>A scripting error occurred.</value>
<comment></comment>
</data>
</data>
<data name="EE_ScriptError_ParsingSyntax" xml:space="preserve">
<value>Incorrect syntax was encountered while {0} was being parsed.</value>
<comment></comment>
</data>
</data>
<data name="EE_ScriptError_FatalError" xml:space="preserve">
<value>A fatal error occurred.</value>
<comment></comment>
</data>
</data>
<data name="EE_ExecutionInfo_FinalizingLoop" xml:space="preserve">
<value>Execution completed {0} times...</value>
<comment></comment>
</data>
</data>
<data name="EE_ExecutionInfo_QueryCancelledbyUser" xml:space="preserve">
<value>You cancelled the query.</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchExecutionError_Halting" xml:space="preserve">
<value>An error occurred while the batch was being executed.</value>
<comment></comment>
</data>
</data>
<data name="EE_BatchExecutionError_Ignoring" xml:space="preserve">
<value>An error occurred while the batch was being executed, but the error has been ignored.</value>
<comment></comment>
</data>
</data>
<data name="EE_ExecutionInfo_InitilizingLoop" xml:space="preserve">
<value>Starting execution loop of {0} times...</value>
<comment></comment>
</data>
</data>
<data name="EE_ExecutionError_CommandNotSupported" xml:space="preserve">
<value>Command {0} is not supported.</value>
<comment></comment>
</data>
</data>
<data name="EE_ExecutionError_VariableNotFound" xml:space="preserve">
<value>The variable {0} could not be found.</value>
<comment></comment>
</data>
</data>
<data name="BatchParserWrapperExecutionEngineError" xml:space="preserve">
<value>SQL Execution error: {0}</value>
<comment></comment>
</data>
</data>
<data name="BatchParserWrapperExecutionError" xml:space="preserve">
<value>Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}</value>
<comment></comment>
</data>
</data>
<data name="BatchParserWrapperExecutionEngineBatchMessage" xml:space="preserve">
<value>Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}</value>
<comment></comment>
</data>
</data>
<data name="BatchParserWrapperExecutionEngineBatchResultSetProcessing" xml:space="preserve">
<value>Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}</value>
<comment></comment>
</data>
</data>
<data name="BatchParserWrapperExecutionEngineBatchResultSetFinished" xml:space="preserve">
<value>Batch parser wrapper execution engine batch ResultSet finished.</value>
<comment></comment>
</data>
</data>
<data name="BatchParserWrapperExecutionEngineBatchCancelling" xml:space="preserve">
<value>Canceling batch parser wrapper batch execution.</value>
<comment></comment>
</data>
</data>
<data name="EE_ScriptError_Warning" xml:space="preserve">
<value>Scripting warning.</value>
<comment></comment>
</data>
</data>
<data name="TroubleshootingAssistanceMessage" xml:space="preserve">
<value>For more information about this error, see the troubleshooting topics in the product documentation.</value>
<comment></comment>
</data>
</data>
<data name="BatchParser_CircularReference" xml:space="preserve">
<value>File '{0}' recursively included.</value>
<comment></comment>
</data>
</data>
<data name="BatchParser_CommentNotTerminated" xml:space="preserve">
<value>Missing end comment mark '*/'.</value>
<comment></comment>
</data>
</data>
<data name="BatchParser_StringNotTerminated" xml:space="preserve">
<value>Unclosed quotation mark after the character string.</value>
<comment></comment>
</data>
</data>
<data name="BatchParser_IncorrectSyntax" xml:space="preserve">
<value>Incorrect syntax was encountered while parsing '{0}'.</value>
<comment></comment>
</data>
</data>
<data name="BatchParser_VariableNotDefined" xml:space="preserve">
<value>Variable {0} is not defined.</value>
<comment></comment>
</data>
</data>
<data name="TestLocalizationConstant" xml:space="preserve">
<value>EN_LOCALIZATION</value>
<comment></comment>
</data>
</data>
<data name="SqlScriptFormatterDecimalMissingPrecision" xml:space="preserve">
<value>Decimal column is missing numeric precision or numeric scale</value>
<comment></comment>
</data>
</root>
</data>
</root>

View File

@@ -164,10 +164,22 @@ WorkspaceServiceBufferPositionOutOfOrder(int sLine, int sCol, int eLine, int eCo
############################################################################
# Edit Data Service
EditDataObjectNotFound = Table or view requested for edit could not be found
EditDataSessionNotFound = Edit session does not exist.
EditDataSessionAlreadyExists = Edit session already exists.
EditDataSessionNotInitialized = Edit session has not been initialized
EditDataSessionAlreadyInitialized = Edit session has already been initialized
EditDataSessionAlreadyInitializing = Edit session has already been initialized or is in the process of initializing
EditDataUnsupportedObjectType(string typeName) = Database object {0} cannot be used for editing.
EditDataQueryFailed = Query execution failed, see messages for details
EditDataQueryNotCompleted = Query has not completed execution
EditDataQueryImproperResultSets = Query did not generate exactly one result set

View File

@@ -561,6 +561,36 @@
<target state="new">NULL is not allowed for this column</target>
<note></note>
</trans-unit>
<trans-unit id="EditDataObjectNotFound">
<source>Table or view requested for edit could not be found</source>
<target state="new">Table or view requested to edit could not be found</target>
<note></note>
</trans-unit>
<trans-unit id="EditDataSessionAlreadyExists">
<source>Edit session already exists.</source>
<target state="new">Edit session already exists.</target>
<note></note>
</trans-unit>
<trans-unit id="EditDataSessionNotInitialized">
<source>Edit session has not been initialized</source>
<target state="new">Edit session has not been initialized</target>
<note></note>
</trans-unit>
<trans-unit id="EditDataSessionAlreadyInitialized">
<source>Edit session has already been initialized</source>
<target state="new">Edit session has already been initialized</target>
<note></note>
</trans-unit>
<trans-unit id="EditDataSessionAlreadyInitializing">
<source>Edit session has already been initialized or is in the process of initializing</source>
<target state="new">Edit session has already been initialized or is in the process of initializing</target>
<note></note>
</trans-unit>
<trans-unit id="EditDataQueryFailed">
<source>Query execution failed, see messages for details</source>
<target state="new">Query execution failed, see messages for details</target>
<note></note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -29,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
AddSupportedType(DeclarationType.Synonym, GetSynonymScripts, "Synonym", "");
AddSupportedType(DeclarationType.ScalarValuedFunction, GetScalarValuedFunctionScripts, "Function", "scalar-valued function");
AddSupportedType(DeclarationType.TableValuedFunction, GetTableValuedFunctionScripts, "Function", "table-valued function");
}
}
/// <summary>
/// Script a Table using SMO
@@ -50,7 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetTableScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a View using SMO
@@ -71,7 +71,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetViewScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a StoredProcedure using SMO
@@ -92,7 +92,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetStoredProcedureScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a UserDefinedDataType using SMO
@@ -113,7 +113,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetUserDefinedDataTypeScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a UserDefinedTableType using SMO
@@ -134,7 +134,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetUserDefinedTableTypeScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a Synonym using SMO
@@ -155,7 +155,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetSynonymScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a ScalarValuedFunction using SMO
@@ -176,7 +176,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetScalarValuedFunctionScripts : " + ex.Message);
return null;
}
}
}
/// <summary>
/// Script a TableValuedFunction using SMO
@@ -197,7 +197,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetTableValuedFunctionScripts : " + ex.Message);
return null;
}
}
}
}
}

View File

@@ -7,13 +7,13 @@ using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.EditData;
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Microsoft.SqlTools.Utility;
using Moq;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
@@ -22,18 +22,49 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
public const string OwnerUri = "testFile";
public static IEditTableMetadata GetStandardMetadata(DbColumn[] columns, bool allKeys = true, bool isMemoryOptimized = false)
public static async Task<EditSession> GetCustomSession(Query q, EditTableMetadata etm)
{
// Create a Column Metadata Provider
// Mock metadata factory
Mock<IEditMetadataFactory> metaFactory = new Mock<IEditMetadataFactory>();
metaFactory
.Setup(f => f.GetObjectMetadata(It.IsAny<DbConnection>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(etm);
EditSession session = new EditSession(metaFactory.Object, "tbl", "tbl");
EditSession.Connector connector = () => Task.FromResult<DbConnection>(null);
EditSession.QueryRunner queryRunner = (s) => Task.FromResult(new EditSession.EditSessionQueryExecutionState(q));
session.Initialize(connector, queryRunner, () => Task.FromResult(0), (e) => Task.FromResult(0));
await session.InitializeTask;
return session;
}
public static EditTableMetadata GetStandardMetadata(DbColumn[] columns, bool isMemoryOptimized = false)
{
// Create column metadata providers
var columnMetas = columns.Select((c, i) =>
new EditColumnWrapper
{
var ecm = new EditColumnMetadata
{
DbColumn = new DbColumnWrapper(c),
EscapedName = c.ColumnName,
Ordinal = i,
IsKey = c.IsIdentity.HasTrue()
}).ToArray();
return GetMetadataProvider(columnMetas, allKeys, isMemoryOptimized);
Ordinal = i
};
return ecm;
}).ToArray();
// Create column wrappers
var columnWrappers = columns.Select(c => new DbColumnWrapper(c)).ToArray();
// Create the table metadata
EditTableMetadata editTableMetadata = new EditTableMetadata
{
Columns = columnMetas,
EscapedMultipartName = "tbl",
IsMemoryOptimized = isMemoryOptimized
};
editTableMetadata.Extend(columnWrappers);
return editTableMetadata;
}
public static DbColumn[] GetColumns(bool includeIdentity)
@@ -42,7 +73,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
if (includeIdentity)
{
columns.Add(new TestDbColumn("id", true));
columns.Add(new TestDbColumn("id") {IsKey = true, IsIdentity = true, IsAutoIncrement = true});
}
for (int i = 0; i < 3; i++)
@@ -52,7 +83,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
return columns.ToArray();
}
public static ResultSet GetResultSet(DbColumn[] columns, bool includeIdentity, int rowCount = 1)
public static async Task<Query> GetQuery(DbColumn[] columns, bool includIdentity, int rowCount = 1)
{
Query q = QueryExecution.Common.GetBasicExecutedQuery();
q.Batches[0].ResultSets[0] = await GetResultSet(columns, includIdentity, rowCount);
return q;
}
public static async Task<ResultSet> GetResultSet(DbColumn[] columns, bool includeIdentity, int rowCount = 1)
{
IEnumerable<object[]> rows = includeIdentity
? Enumerable.Repeat(new object[] { "id", "1", "2", "3" }, rowCount)
@@ -60,7 +98,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
var testResultSet = new TestResultSet(columns, rows);
var reader = new TestDbDataReader(new[] { testResultSet });
var resultSet = new ResultSet(0, 0, MemoryFileSystem.GetFileStreamFactory());
resultSet.ReadResultToEnd(reader, CancellationToken.None).Wait();
await resultSet.ReadResultToEnd(reader, CancellationToken.None);
return resultSet;
}
@@ -82,26 +120,5 @@ 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<IEditTableMetadata>();
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;
}
}
}

View File

@@ -22,13 +22,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public class RowCreateTests
{
[Fact]
public void RowCreateConstruction()
public async Task RowCreateConstruction()
{
// Setup: Create the values to store
const long rowId = 100;
DbColumn[] columns = Common.GetColumns(false);
ResultSet rs = Common.GetResultSet(columns, false);
IEditTableMetadata etm = Common.GetStandardMetadata(columns);
ResultSet rs = await Common.GetResultSet(columns, false);
EditTableMetadata etm = Common.GetStandardMetadata(columns);
// If: I create a RowCreate instance
RowCreate rc = new RowCreate(rowId, rs, etm);
@@ -42,13 +42,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Theory]
[InlineData(true)]
[InlineData(false)]
public void GetScript(bool includeIdentity)
public async Task GetScript(bool includeIdentity)
{
// Setup: Generate the parameters for the row create
const long rowId = 100;
DbColumn[] columns = Common.GetColumns(includeIdentity);
ResultSet rs = Common.GetResultSet(columns, includeIdentity);
IEditTableMetadata etm = Common.GetStandardMetadata(columns);
ResultSet rs = await Common.GetResultSet(columns, includeIdentity);
EditTableMetadata etm = Common.GetStandardMetadata(columns);
// If: I ask for a script to be generated without an identity column
RowCreate rc = new RowCreate(rowId, rs, etm);
@@ -74,10 +74,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetScriptMissingCell()
public async Task GetScriptMissingCell()
{
// Setup: Generate the parameters for the row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I ask for a script to be generated without setting any values
// Then: An exception should be thrown for missing cells
@@ -93,8 +93,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// ... Generate the parameters for the row create
const long rowId = 100;
DbColumn[] columns = Common.GetColumns(includeIdentity);
ResultSet rs = Common.GetResultSet(columns, includeIdentity);
IEditTableMetadata etm = Common.GetStandardMetadata(columns);
ResultSet rs = await Common.GetResultSet(columns, includeIdentity);
EditTableMetadata etm = Common.GetStandardMetadata(columns);
// ... Setup a db reader for the result of an insert
var newRowReader = Common.GetNewRowDataReader(columns, includeIdentity);
@@ -110,13 +110,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Theory]
[InlineData(true)]
[InlineData(false)]
public void GetCommand(bool includeIdentity)
public async Task GetCommand(bool includeIdentity)
{
// Setup:
// ... Create a row create with cell updates
const long rowId = 100;
var columns = Common.GetColumns(includeIdentity);
var rs = Common.GetResultSet(columns, includeIdentity);
var rs = await Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns);
RowCreate rc = new RowCreate(rowId, rs, etm);
Common.AddCells(rc, includeIdentity);
@@ -159,10 +159,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetCommandNullConnection()
public async Task GetCommandNullConnection()
{
// Setup: Create a row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I attempt to create a command with a null connection
@@ -171,10 +171,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetCommandMissingCell()
public async Task GetCommandMissingCell()
{
// Setup: Generate the parameters for the row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
var mockConn = new TestSqlConnection(null);
// If: I ask for a script to be generated without setting any values
@@ -183,10 +183,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetEditRowNoAdditions()
public async Task GetEditRowNoAdditions()
{
// Setup: Generate a standard row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I request an edit row from the row create
EditRow er = rc.GetEditRow(null);
@@ -209,10 +209,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetEditRowWithAdditions()
public async Task GetEditRowWithAdditions()
{
// Setp: Generate a row create with a cell added to it
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
rc.SetCell(0, "foo");
// If: I request an edit row from the row create
@@ -244,20 +244,20 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[InlineData(-1)] // Negative
[InlineData(3)] // At edge of acceptable values
[InlineData(100)] // Way too large value
public void SetCellOutOfRange(int columnId)
public async Task SetCellOutOfRange(int columnId)
{
// Setup: Generate a row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I attempt to set a cell on a column that is out of range, I should get an exception
Assert.Throws<ArgumentOutOfRangeException>(() => rc.SetCell(columnId, string.Empty));
}
[Fact]
public void SetCellNoChange()
public async Task SetCellNoChange()
{
// Setup: Generate a row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I set a cell in the newly created row to something that doesn't need changing
EditUpdateCellResult eucr = rc.SetCell(0, "1");
@@ -278,16 +278,20 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SetCellHasCorrections()
public async Task SetCellHasCorrections()
{
// Setup:
// ... Generate a result set with a single binary column
DbColumn[] cols = {new TestDbColumn("bin", "binary", typeof(byte[]))};
DbColumn[] cols = {new TestDbColumn
{
DataType = typeof(byte[]),
DataTypeName = "binary"
}};
object[][] rows = {};
var testResultSet = new TestResultSet(cols, rows);
var testReader = new TestDbDataReader(new[] {testResultSet});
var rs = new ResultSet(0, 0, MemoryFileSystem.GetFileStreamFactory());
rs.ReadResultToEnd(testReader, CancellationToken.None).Wait();
await rs.ReadResultToEnd(testReader, CancellationToken.None);
// ... Generate the metadata
var etm = Common.GetStandardMetadata(cols);
@@ -314,10 +318,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SetCellNull()
public async Task SetCellNull()
{
// Setup: Generate a row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I set a cell in the newly created row to null
EditUpdateCellResult eucr = rc.SetCell(0, "NULL");
@@ -341,10 +345,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[InlineData(-1)] // Negative
[InlineData(3)] // At edge of acceptable values
[InlineData(100)] // Way too large value
public void RevertCellOutOfRange(int columnId)
public async Task RevertCellOutOfRange(int columnId)
{
// Setup: Generate the row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I attempt to revert a cell that is out of range
// Then: I should get an exception
@@ -352,10 +356,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void RevertCellNotSet()
public async Task RevertCellNotSet()
{
// Setup: Generate the row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
// If: I attempt to revert a cell that has not been set
string result = rc.RevertCell(0);
@@ -369,10 +373,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void RevertCellThatWasSet()
public async Task RevertCellThatWasSet()
{
// Setup: Generate the row create
RowCreate rc = GetStandardRowCreate();
RowCreate rc = await GetStandardRowCreate();
rc.SetCell(0, "1");
// If: I attempt to revert a cell that was set
@@ -386,10 +390,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
Assert.Null(rc.newCells[0]);
}
private static RowCreate GetStandardRowCreate()
private static async Task<RowCreate> GetStandardRowCreate()
{
var cols = Common.GetColumns(false);
var rs = Common.GetResultSet(cols, false);
var rs = await Common.GetResultSet(cols, false);
var etm = Common.GetStandardMetadata(cols);
return new RowCreate(100, rs, etm);
}

View File

@@ -21,12 +21,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public class RowDeleteTests
{
[Fact]
public void RowDeleteConstruction()
public async Task RowDeleteConstruction()
{
// Setup: Create the values to store
DbColumn[] columns = Common.GetColumns(true);
ResultSet rs = Common.GetResultSet(columns, true);
IEditTableMetadata etm = Common.GetStandardMetadata(columns, false);
ResultSet rs = await Common.GetResultSet(columns, true);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
// If: I create a RowCreate instance
RowDelete rc = new RowDelete(100, rs, etm);
@@ -40,11 +40,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Theory]
[InlineData(true)]
[InlineData(false)]
public void GetScriptTest(bool isMemoryOptimized)
public async Task GetScriptTest(bool isMemoryOptimized)
{
DbColumn[] columns = Common.GetColumns(true);
ResultSet rs = Common.GetResultSet(columns, true);
IEditTableMetadata etm = Common.GetStandardMetadata(columns, false, isMemoryOptimized);
ResultSet rs = await Common.GetResultSet(columns, true);
EditTableMetadata etm = Common.GetStandardMetadata(columns, 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
{
// Setup: Generate the parameters for the row delete object
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var rs = await Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
// If: I ask for the change to be applied
@@ -84,13 +84,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[InlineData(false, true)]
[InlineData(true, false)]
[InlineData(false, false)]
public void GetCommand(bool includeIdentity, bool isMemoryOptimized)
public async Task GetCommand(bool includeIdentity, bool isMemoryOptimized)
{
// Setup:
// ... Create a row delete
var columns = Common.GetColumns(includeIdentity);
var rs = Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns, !includeIdentity, isMemoryOptimized);
var rs = await Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns, isMemoryOptimized);
RowDelete rd = new RowDelete(0, rs, etm);
// ... Mock db connection for building the command
@@ -128,10 +128,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetCommandNullConnection()
public async Task GetCommandNullConnection()
{
// Setup: Create a row delete
RowDelete rd = GetStandardRowDelete();
RowDelete rd = await GetStandardRowDelete();
// If: I attempt to create a command with a null connection
// Then: It should throw an exception
@@ -139,11 +139,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetEditRow()
public async Task GetEditRow()
{
// Setup: Create a row delete
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var rs = await Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
RowDelete rd = new RowDelete(0, rs, etm);
@@ -173,10 +173,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetEditNullRow()
public async Task GetEditNullRow()
{
// Setup: Create a row delete
RowDelete rd = GetStandardRowDelete();
RowDelete rd = await GetStandardRowDelete();
// If: I attempt to get an edit row with a null cached row
// Then: I should get an exception
@@ -184,10 +184,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SetCell()
public async Task SetCell()
{
// Setup: Create a row delete
RowDelete rd = GetStandardRowDelete();
RowDelete rd = await GetStandardRowDelete();
// If: I set a cell on a delete row edit
// Then: It should throw as invalid operation
@@ -195,20 +195,20 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void RevertCell()
public async Task RevertCell()
{
// Setup: Create a row delete
RowDelete rd = GetStandardRowDelete();
RowDelete rd = await GetStandardRowDelete();
// If: I revert a cell on a delete row edit
// Then: It should throw
Assert.Throws<InvalidOperationException>(() => rd.RevertCell(0));
}
private RowDelete GetStandardRowDelete()
private async Task<RowDelete> GetStandardRowDelete()
{
var cols = Common.GetColumns(false);
var rs = Common.GetResultSet(cols, false);
var rs = await Common.GetResultSet(cols, false);
var etm = Common.GetStandardMetadata(cols);
return new RowDelete(0, rs, etm);
}

View File

@@ -25,12 +25,16 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
[Theory]
[InlineData(-1)] // Negative index
[InlineData(2)] // Equal to count of columns
[InlineData(100)] // Index larger than number of columns
public void ValidateUpdatableColumnOutOfRange(int columnId)
public async Task ValidateUpdatableColumnOutOfRange(int columnId)
{
// Setup: Create a result set
ResultSet rs = GetResultSet(
new DbColumn[] { new TestDbColumn("id", true), new TestDbColumn("col1")},
ResultSet rs = await GetResultSet(
new DbColumn[] {
new TestDbColumn("id") {IsKey = true, IsAutoIncrement = true, IsIdentity = true},
new TestDbColumn("col1")
},
new object[] { "id", "1" });
// If: I validate a column ID that is out of range
@@ -40,11 +44,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void ValidateUpdatableColumnNotUpdatable()
public async Task ValidateUpdatableColumnNotUpdatable()
{
// Setup: Create a result set with an identity column
ResultSet rs = GetResultSet(
new DbColumn[] { new TestDbColumn("id", true), new TestDbColumn("col1") },
ResultSet rs = await GetResultSet(
new DbColumn[] {
new TestDbColumn("id") {IsKey = true, IsAutoIncrement = true, IsIdentity = true},
new TestDbColumn("col1")
},
new object[] { "id", "1" });
// If: I validate a column ID that is not updatable
@@ -55,12 +62,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Theory]
[MemberData(nameof(GetWhereClauseIsNotNullData))]
public void GetWhereClauseSimple(DbColumn col, object val, string nullClause)
public async Task GetWhereClauseSimple(DbColumn col, object val, string nullClause)
{
// 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.GetStandardMetadata(cols);
ResultSet rs = await GetResultSet(cols, new[] {val});
EditTableMetadata etm = Common.GetStandardMetadata(cols);
RowEditTester rt = new RowEditTester(rs, etm);
rt.ValidateWhereClauseSingleKey(nullClause);
@@ -71,42 +78,69 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
get
{
yield return new object[] {new TestDbColumn("col"), DBNull.Value, "IS NULL"};
yield return new object[] {new TestDbColumn("col", "VARBINARY", typeof(byte[])), new byte[5], "IS NOT NULL"};
yield return new object[] {new TestDbColumn("col", "TEXT", typeof(string)), "abc", "IS NOT NULL"};
yield return new object[] {new TestDbColumn("col", "NTEXT", typeof(string)), "abc", "IS NOT NULL"};
yield return new object[] {
new TestDbColumn
{
DataTypeName = "BINARY",
DataType = typeof(byte[])
},
new byte[5],
"IS NOT NULL"
};
yield return new object[]
{
new TestDbColumn
{
DataType = typeof(string),
DataTypeName = "TEXT"
},
"abc",
"IS NOT NULL"
};
yield return new object[]
{
new TestDbColumn
{
DataType = typeof(string),
DataTypeName = "NTEXT",
},
"abc",
"IS NOT NULL"
};
}
}
[Fact]
public void GetWhereClauseMultipleKeyColumns()
public async Task GetWhereClauseMultipleKeyColumns()
{
// 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.GetStandardMetadata(cols);
ResultSet rs = await GetResultSet(cols, new object[] {"abc", "def"});
EditTableMetadata etm = Common.GetStandardMetadata(cols);
RowEditTester rt = new RowEditTester(rs, etm);
rt.ValidateWhereClauseMultipleKeys();
}
[Fact]
public void GetWhereClauseNoKeyColumns()
public async Task GetWhereClauseNoKeyColumns()
{
// 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.GetStandardMetadata(new DbColumn[] {});
ResultSet rs = await GetResultSet(cols, new object[] {"abc", "def"});
EditTableMetadata etm = Common.GetStandardMetadata(new DbColumn[] {});
RowEditTester rt = new RowEditTester(rs, etm);
rt.ValidateWhereClauseNoKeys();
}
[Fact]
public void SortingByTypeTest()
public async Task SortingByTypeTest()
{
// Setup: Create a result set and metadata we can reuse
var cols = Common.GetColumns(false);
var rs = Common.GetResultSet(cols, false);
var rs = await Common.GetResultSet(cols, false);
var etm = Common.GetStandardMetadata(cols);
// If: I request to sort a list of the three different edit operations
@@ -124,11 +158,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SortingUpdatesByRowIdTest()
public async Task SortingUpdatesByRowIdTest()
{
// Setup: Create a result set and metadata we can reuse
var cols = Common.GetColumns(false);
var rs = Common.GetResultSet(cols, false, 4);
var rs = await Common.GetResultSet(cols, false, 4);
var etm = Common.GetStandardMetadata(cols);
// If: I sort 3 edit operations of the same type
@@ -147,11 +181,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SortingCreatesByRowIdTest()
public async Task SortingCreatesByRowIdTest()
{
// Setup: Create a result set and metadata we can reuse
var cols = Common.GetColumns(false);
var rs = Common.GetResultSet(cols, false);
var rs = await Common.GetResultSet(cols, false);
var etm = Common.GetStandardMetadata(cols);
// If: I sort 3 edit operations of the same type
@@ -170,11 +204,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SortingDeletesByRowIdTest()
public async Task SortingDeletesByRowIdTest()
{
// Setup: Create a result set and metadata we can reuse
var cols = Common.GetColumns(false);
var rs = Common.GetResultSet(cols, false);
var rs = await Common.GetResultSet(cols, false);
var etm = Common.GetStandardMetadata(cols);
// If: I sort 3 delete operations of the same type
@@ -192,19 +226,19 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
Assert.Equal(1, rowEdits[2].RowId);
}
private static ResultSet GetResultSet(DbColumn[] columns, object[] row)
private static async Task<ResultSet> GetResultSet(DbColumn[] columns, object[] row)
{
object[][] rows = {row};
var testResultSet = new TestResultSet(columns, rows);
var testReader = new TestDbDataReader(new [] {testResultSet});
var resultSet = new ResultSet(0,0, MemoryFileSystem.GetFileStreamFactory());
resultSet.ReadResultToEnd(testReader, CancellationToken.None).Wait();
await resultSet.ReadResultToEnd(testReader, CancellationToken.None);
return resultSet;
}
private class RowEditTester : RowEditBase
{
public RowEditTester(ResultSet rs, IEditTableMetadata meta) : base(0, rs, meta) { }
public RowEditTester(ResultSet rs, EditTableMetadata meta) : base(0, rs, meta) { }
public void ValidateColumn(int columnId)
{

View File

@@ -28,7 +28,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.GetStandardMetadata(rs.Columns);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
// If: I create a RowUpdate instance
RowUpdate rc = new RowUpdate(rowId, rs, etm);
@@ -40,10 +40,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SetCell()
public async Task SetCell()
{
// Setup: Create a row update
RowUpdate ru = GetStandardRowUpdate();
RowUpdate ru = await GetStandardRowUpdate();
// If: I set a cell that can be updated
EditUpdateCellResult eucr = ru.SetCell(0, "col1");
@@ -69,7 +69,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup:
// ... Generate a result set with a single binary column
DbColumn[] cols = { new TestDbColumn("bin", "binary", typeof(byte[])) };
DbColumn[] cols =
{
new TestDbColumn
{
DataType = typeof(byte[]),
DataTypeName = "binary"
}
};
object[][] rows = { new object[]{new byte[] {0x00}}};
var testResultSet = new TestResultSet(cols, rows);
var testReader = new TestDbDataReader(new[] { testResultSet });
@@ -102,12 +109,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void SetCellImplicitRevertTest()
public async Task SetCellImplicitRevertTest()
{
// Setup: Create a fake table to update
DbColumn[] columns = Common.GetColumns(true);
ResultSet rs = Common.GetResultSet(columns, true);
IEditTableMetadata etm = Common.GetStandardMetadata(columns);
ResultSet rs = await Common.GetResultSet(columns, true);
EditTableMetadata etm = Common.GetStandardMetadata(columns);
// If:
// ... I add updates to all the cells in the row
@@ -139,12 +146,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Theory]
[InlineData(true)]
[InlineData(false)]
public void GetScriptTest(bool isMemoryOptimized)
public async Task GetScriptTest(bool isMemoryOptimized)
{
// Setup: Create a fake table to update
DbColumn[] columns = Common.GetColumns(true);
ResultSet rs = Common.GetResultSet(columns, true);
IEditTableMetadata etm = Common.GetStandardMetadata(columns, false, isMemoryOptimized);
ResultSet rs = await Common.GetResultSet(columns, true);
EditTableMetadata etm = Common.GetStandardMetadata(columns, isMemoryOptimized);
// If: I ask for a script to be generated for update
RowUpdate ru = new RowUpdate(0, rs, etm);
@@ -177,13 +184,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
public void GetCommand(bool includeIdentity, bool isMemoryOptimized)
public async Task GetCommand(bool includeIdentity, bool isMemoryOptimized)
{
// Setup:
// ... Create a row update with cell updates
var columns = Common.GetColumns(includeIdentity);
var rs = Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns, !includeIdentity, isMemoryOptimized);
var rs = await Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns, isMemoryOptimized);
RowUpdate ru = new RowUpdate(0, rs, etm);
Common.AddCells(ru, includeIdentity);
@@ -231,10 +238,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetCommandNullConnection()
public async Task GetCommandNullConnection()
{
// Setup: Create a row update
RowUpdate ru = GetStandardRowUpdate();
RowUpdate ru = await GetStandardRowUpdate();
// If: I attempt to create a command with a null connection
// Then: It should throw an exception
@@ -242,11 +249,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetEditRow()
public async Task GetEditRow()
{
// Setup: Create a row update with a cell set
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var rs = await Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
RowUpdate ru = new RowUpdate(0, rs, etm);
ru.SetCell(0, "foo");
@@ -282,10 +289,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void GetEditNullRow()
public async Task GetEditNullRow()
{
// Setup: Create a row update
RowUpdate ru = GetStandardRowUpdate();
RowUpdate ru = await GetStandardRowUpdate();
// If: I attempt to get an edit row with a null cached row
// Then: I should get an exception
@@ -300,8 +307,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// Setup:
// ... Create a row update (no cell updates needed)
var columns = Common.GetColumns(includeIdentity);
var rs = Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns, !includeIdentity);
var rs = await Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns);
RowUpdate ru = new RowUpdate(0, rs, etm);
long oldBytesWritten = rs.totalBytesWritten;
@@ -323,8 +330,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// Setup:
// ... Create a row update (no cell updates needed)
var columns = Common.GetColumns(true);
var rs = Common.GetResultSet(columns, true);
var etm = Common.GetStandardMetadata(columns, false);
var rs = await Common.GetResultSet(columns, true);
var etm = Common.GetStandardMetadata(columns);
RowUpdate ru = new RowUpdate(0, rs, etm);
// If: I ask for the changes to be applied with a null db reader
@@ -336,12 +343,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[InlineData(-1)] // Negative
[InlineData(3)] // At edge of acceptable values
[InlineData(100)] // Way too large value
public void RevertCellOutOfRange(int columnId)
public async Task RevertCellOutOfRange(int columnId)
{
// Setup:
// ... Create a row update (no cell updates needed)
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var rs = await Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
RowUpdate ru = new RowUpdate(0, rs, etm);
@@ -351,12 +358,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void RevertCellNotSet()
public async Task RevertCellNotSet()
{
// Setup:
// ... Create a row update (no cell updates needed)
var columns = Common.GetColumns(true);
var rs = Common.GetResultSet(columns, true);
var rs = await Common.GetResultSet(columns, true);
var etm = Common.GetStandardMetadata(columns);
RowUpdate ru = new RowUpdate(0, rs, etm);
@@ -371,12 +378,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void RevertCellThatWasSet()
public async Task RevertCellThatWasSet()
{
// Setup:
// ... Create a row update
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var rs = await Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
RowUpdate ru = new RowUpdate(0, rs, etm);
ru.SetCell(0, "1");
@@ -392,10 +399,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
Assert.DoesNotContain(0, ru.cellUpdates.Keys);
}
private RowUpdate GetStandardRowUpdate()
private async Task<RowUpdate> GetStandardRowUpdate()
{
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var rs = await Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
return new RowUpdate(0, rs, etm);
}

View File

@@ -50,7 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// Setup:
// ... Create an edit data service with a session
var eds = new EditDataService(null, null, null);
eds.ActiveSessions[Common.OwnerUri] = GetDefaultSession();
eds.ActiveSessions[Common.OwnerUri] = await GetDefaultSession();
// ... Create a session param that returns the common owner uri
var mockParams = new EditCreateRowParams { OwnerUri = Common.OwnerUri };
@@ -94,7 +94,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup: Create an edit data service with a session
var eds = new EditDataService(null, null, null);
eds.ActiveSessions[Common.OwnerUri] = GetDefaultSession();
eds.ActiveSessions[Common.OwnerUri] = await GetDefaultSession();
// If: I ask to dispose of an existing session
var efv = new EventFlowValidator<EditDisposeResult>()
@@ -117,7 +117,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup: Create an edit data service with a session
var eds = new EditDataService(null, null, null);
eds.ActiveSessions[Constants.OwnerUri] = GetDefaultSession();
eds.ActiveSessions[Constants.OwnerUri] = await GetDefaultSession();
// If: I validly ask to delete a row
var efv = new EventFlowValidator<EditDeleteRowResult>()
@@ -139,7 +139,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup: Create an edit data service with a session
var eds = new EditDataService(null, null, null);
eds.ActiveSessions[Constants.OwnerUri] = GetDefaultSession();
eds.ActiveSessions[Constants.OwnerUri] = await GetDefaultSession();
// If: I ask to create a row from a non existant session
var efv = new EventFlowValidator<EditCreateRowResult>()
@@ -161,7 +161,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup: Create an edit data service with a session that has an pending edit
var eds = new EditDataService(null, null, null);
var session = GetDefaultSession();
var session = await GetDefaultSession();
session.EditCache[0] = new Mock<RowEditBase>().Object;
eds.ActiveSessions[Constants.OwnerUri] = session;
@@ -185,7 +185,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup: Create an edit data service with a session
var eds = new EditDataService(null, null, null);
var session = GetDefaultSession();
var session = await GetDefaultSession();
eds.ActiveSessions[Constants.OwnerUri] = session;
var edit = new Mock<RowEditBase>();
edit.Setup(e => e.SetCell(It.IsAny<int>(), It.IsAny<string>())).Returns(new EditUpdateCellResult
@@ -223,7 +223,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// Setup: Create an edit data service with a session
// Setup: Create an edit data service with a session
var eds = new EditDataService(null, null, null);
var session = GetDefaultSession();
var session = await GetDefaultSession();
eds.ActiveSessions[Constants.OwnerUri] = session;
// If: I validly ask for rows
@@ -277,119 +277,15 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// ... There should not be a session
Assert.Empty(eds.ActiveSessions);
// ... There should not be a wait handler
Assert.Empty(eds.InitializeWaitHandles);
}
[Fact]
public async Task InitializeInProgress()
{
// Setup: Create an edit data service with an "in-progress initialize"
var eds = new EditDataService(null, null, null);
eds.InitializeWaitHandles[Common.OwnerUri] = new TaskCompletionSource<bool>();
// If:
// ... I ask to initialize a session when an initialize task is already in progress
var initParams = new EditInitializeParams
{
ObjectName = "table",
OwnerUri = Common.OwnerUri,
ObjectType = "table"
};
var efv = new EventFlowValidator<EditInitializeResult>()
.AddErrorValidation<string>(Assert.NotNull)
.Complete();
await eds.HandleInitializeRequest(initParams, efv.Object);
// Then:
// ... An error event should have been raised
efv.Validate();
// ... There should not be a session
Assert.Empty(eds.ActiveSessions);
}
[Fact]
public async Task InitializeQueryCreateFailed()
{
// Setup:
// ... Create a query execution service that will throw on creation of the query
var qes = QueryExecution.Common.GetPrimedExecutionService(null, false, false, null);
// ... Create an edit data service that uses the mocked up query service
var eds = new EditDataService(qes, null, null);
// If:
// ... I initialize a session
var initParams = new EditInitializeParams
{
ObjectName = "table",
OwnerUri = Common.OwnerUri,
ObjectType = "table"
};
var efv = new EventFlowValidator<EditInitializeResult>()
.AddErrorValidation<string>(Assert.NotEmpty)
.Complete();
await eds.HandleInitializeRequest(initParams, efv.Object);
// Then:
// ... We should have gotten an error back
efv.Validate();
// ... There should not be any sessions created
Assert.Empty(eds.ActiveSessions);
// ... There should not be a wait handle
Assert.Empty(eds.InitializeWaitHandles);
}
[Fact]
public async Task InitializeQueryExecutionFails()
{
// Setup:
// ... Create a query execution service that will throw on execution of the query
var qes = QueryExecution.Common.GetPrimedExecutionService(null, true, true, null);
// ... Create an edit data service that uses the mocked up query service
var eds = new EditDataService(qes, null, null);
// If:
// ... I initialize a session
var initParams = new EditInitializeParams
{
ObjectName = "table",
OwnerUri = Common.OwnerUri,
ObjectType = "table"
};
var efv = new EventFlowValidator<EditInitializeResult>()
.AddResultValidation(Assert.NotNull)
.AddEventValidation(EditSessionReadyEvent.Type, esrp =>
{
Assert.NotNull(esrp);
Assert.False(esrp.Success);
}).Complete();
await eds.HandleInitializeRequest(initParams, efv.Object);
await eds.InitializeWaitHandles[Common.OwnerUri].Task;
// Then:
// ... We should have started execution, but failed
efv.Validate();
// ... There should not be any sessions created
Assert.Empty(eds.ActiveSessions);
// ... There should not be a wait handle. It should have been cleaned up by now
Assert.Empty(eds.InitializeWaitHandles);
}
private static EditSession GetDefaultSession()
private static async Task<EditSession> GetDefaultSession()
{
// ... Create a session with a proper query and metadata
Query q = QueryExecution.Common.GetBasicExecutedQuery();
ResultSet rs = q.Batches[0].ResultSets[0];
IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
EditSession s = await Common.GetCustomSession(q, etm);
return s;
}
}

View File

@@ -28,40 +28,55 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#region Construction Tests
[Fact]
public void SessionConstructionNullQuery()
public void SessionConstructionNullMetadataFactory()
{
// If: I create a session object without a null query
// If: I create a session object with a null metadata factory
// Then: It should throw an exception
Assert.Throws<ArgumentNullException>(() => new EditSession(null, Common.GetStandardMetadata(new DbColumn[] {})));
Assert.Throws<ArgumentNullException>(() => new EditSession(null, Constants.OwnerUri, Constants.OwnerUri));
}
[Fact]
public void SessionConstructionNullMetadataProvider()
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" \t\r\n")]
public void SessionConstructionNullObjectName(string objName)
{
// If: I create a session object without a null metadata provider
// If: I create a session object with a null or whitespace object name
// Then: It should throw an exception
Query q = QueryExecution.Common.GetBasicExecutedQuery();
ResultSet rs = q.Batches[0].ResultSets[0];
Assert.Throws<ArgumentNullException>(() => new EditSession(rs, null));
Mock<IEditMetadataFactory> mockFactory = new Mock<IEditMetadataFactory>();
Assert.Throws<ArgumentException>(() => new EditSession(mockFactory.Object, objName, Constants.OwnerUri));
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" \t\r\n")]
public void SessionConstructionNullObjectType(string objType)
{
// If: I create a session object with a null or whitespace object type
// Then: It should throw an exception
Mock<IEditMetadataFactory> mockFactory = new Mock<IEditMetadataFactory>();
Assert.Throws<ArgumentException>(() => new EditSession(mockFactory.Object, Constants.OwnerUri, objType));
}
[Fact]
public void SessionConstructionValid()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
// If: I create a session object with a proper arguments
Mock<IEditMetadataFactory> mockFactory = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(mockFactory.Object, Constants.OwnerUri, Constants.OwnerUri);
// Then:
// ... The edit cache should exist and be empty
Assert.NotNull(s.EditCache);
Assert.Empty(s.EditCache);
// ... The edit cache should not exist
Assert.Null(s.EditCache);
// ... The session shouldn't be initialized
Assert.False(s.IsInitialized);
Assert.Null(s.EditCache);
Assert.Null(s.CommitTask);
// ... The next row ID should be equivalent to the number of rows in the result set
Assert.Equal(q.Batches[0].ResultSets[0].RowCount, s.NextRowId);
// ... The next row ID should be the default long
Assert.Equal(default(long), s.NextRowId);
}
#endregion
@@ -118,15 +133,28 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#region Create Row Tests
[Fact]
public void CreateRowAddFailure()
public void CreateRowNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to create a row without initializing
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.CreateRow());
}
[Fact]
public async Task CreateRowAddFailure()
{
// NOTE: This scenario should theoretically never occur, but is tested for completeness
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
EditSession s = await Common.GetCustomSession(q, etm);
// ... Add a mock edit to the edit cache to cause the .TryAdd to fail
var mockEdit = new Mock<RowEditBase>().Object;
@@ -145,13 +173,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void CreateRowSuccess()
public async Task CreateRowSuccess()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
EditSession s = await Common.GetCustomSession(q, etm);
// If: I add a row to the session
EditCreateRowResult result = s.CreateRow();
@@ -172,48 +200,48 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void CreateRowDefaultTest()
public async Task CreateRowDefaultTest()
{
// Setup:
// ... We will have 3 columns
DbColumn[] cols =
DbColumnWrapper[] 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
new DbColumnWrapper(new TestDbColumn("col1")), // No default
new DbColumnWrapper(new TestDbColumn("col2")), // Has default (defined below)
new DbColumnWrapper(new TestDbColumn("filler")) // Filler column so we can use the common code
};
// ... Metadata provider will return 3 columns
EditColumnWrapper[] metas =
EditColumnMetadata[] metas =
{
new EditColumnWrapper // No default
new EditColumnMetadata // No default
{
DbColumn = new DbColumnWrapper(cols[0]),
DefaultValue = null,
EscapedName = cols[0].ColumnName,
Ordinal = 0,
IsKey = false
},
new EditColumnWrapper // Has default
new EditColumnMetadata // Has default
{
DbColumn = new DbColumnWrapper(cols[1]),
DefaultValue = "default",
EscapedName = cols[0].ColumnName,
Ordinal = 0,
IsKey = false
},
new EditColumnWrapper()
new EditColumnMetadata()
};
var etm = Common.GetMetadataProvider(metas, true);
var etm = new EditTableMetadata
{
Columns = metas,
EscapedMultipartName = "tbl",
IsMemoryOptimized = false
};
etm.Extend(cols);
// ... Create a result set
var rs = Common.GetResultSet(cols, false, 1);
var q = await Common.GetQuery(cols, false);
// ... Create a session from all this
var session = new EditSession(rs, etm);
EditSession s = await Common.GetCustomSession(q, etm);
// If: I add a row to the session, on a table that has defaults
var result = session.CreateRow();
var result = s.CreateRow();
// Then:
// ... Result should not be null, new row ID should be > 0
@@ -233,13 +261,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Theory]
[MemberData(nameof(RowIdOutOfRangeData))]
public void RowIdOutOfRange(long rowId, Action<EditSession, long> testAction)
public async Task RowIdOutOfRange(long rowId, Action<EditSession, long> testAction)
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// If: I delete a row that is out of range for the result set
// Then: I should get an exception
@@ -253,26 +278,260 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// Delete Row
Action<EditSession, long> delAction = (s, l) => s.DeleteRow(l);
yield return new object[] { -1L, delAction };
yield return new object[] {(long) QueryExecution.Common.StandardRows, delAction};
yield return new object[] { 100L, delAction };
// Update Cell
Action<EditSession, long> upAction = (s, l) => s.UpdateCell(l, 0, null);
yield return new object[] { -1L, upAction };
yield return new object[] {(long) QueryExecution.Common.StandardRows, upAction};
yield return new object[] { 100L, upAction };
// Revert Row
Action<EditSession, long> revertRowAction = (s, l) => s.RevertRow(l);
yield return new object[] {-1L, revertRowAction};
yield return new object[] {0L, revertRowAction}; // This is invalid b/c there isn't an edit pending for this row
yield return new object[] {(long) QueryExecution.Common.StandardRows, revertRowAction};
yield return new object[] {100L, revertRowAction};
// Revert Cell
Action<EditSession, long> revertCellAction = (s, l) => s.RevertCell(l, 0);
yield return new object[] {-1L, revertRowAction};
yield return new object[] {0L, revertRowAction}; // This is invalid b/c there isn't an edit pending for this row
yield return new object[] {(long) QueryExecution.Common.StandardRows, revertRowAction};
yield return new object[] {100L, revertRowAction};
}
}
#region Initialize Tests
[Fact]
public void InitializeAlreadyInitialized()
{
// Setup:
// ... Create a session and fake that it has been initialized
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
s.IsInitialized = true;
// If: I initialize it
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.Initialize(null, null, null, null));
}
[Fact]
public void InitializeAlreadyInitializing()
{
// Setup:
// ... Create a session and fake that it is in progress of initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
s.InitializeTask = new Task(() => { });
// If: I initialize it
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.Initialize(null, null, null, null));
}
[Theory]
[MemberData(nameof(InitializeNullParamsData))]
public void InitializeNullParams(EditSession.Connector c, EditSession.QueryRunner qr,
Func<Task> sh, Func<Exception, Task> fh)
{
// Setup:
// ... Create a session that hasn't been initialized
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I initialize it with a missing parameter
// Then: It should throw an exception
Assert.ThrowsAny<ArgumentException>(() => s.Initialize(c, qr, sh, fh));
}
public static IEnumerable<object> InitializeNullParamsData
{
get
{
yield return new object[] {null, DoNothingQueryRunner, DoNothingSuccessHandler, DoNothingFailureHandler};
yield return new object[] {DoNothingConnector, null, DoNothingSuccessHandler, DoNothingFailureHandler};
yield return new object[] {DoNothingConnector, DoNothingQueryRunner, null, DoNothingFailureHandler};
yield return new object[] {DoNothingConnector, DoNothingQueryRunner, DoNothingSuccessHandler, null};
}
}
[Fact]
public async Task InitializeMetadataFails()
{
// Setup:
// ... Create a metadata factory that throws
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
emf.Setup(f => f.GetObjectMetadata(It.IsAny<DbConnection>(), It.IsAny<string>(), It.IsAny<string>()))
.Throws<Exception>();
// ... Create a session that hasn't been initialized
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// ... Create a mock for verifying the failure handler will be called
var successHandler = DoNothingSuccessMock;
var failureHandler = DoNothingFailureMock;
// If: I initalize the session with a metadata factory that will fail
s.Initialize(DoNothingConnector, DoNothingQueryRunner, successHandler.Object, failureHandler.Object);
await s.InitializeTask;
// Then:
// ... The session should not be initialized
Assert.False(s.IsInitialized);
// ... The failure handler should have been called once
failureHandler.Verify(f => f(It.IsAny<Exception>()), Times.Once);
// ... The success handler should not have been called at all
successHandler.Verify(f => f(), Times.Never);
}
[Fact]
public async Task InitializeQueryFailException()
{
// Setup:
// ... Create a metadata factory that will return some generic column information
var b = QueryExecution.Common.GetBasicExecutedBatch();
var etm = Common.GetStandardMetadata(b.ResultSets[0].Columns);
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
emf.Setup(f => f.GetObjectMetadata(It.IsAny<DbConnection>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(etm);
// ... Create a session that hasn't been initialized
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// ... Create a query runner that will fail via exception
Mock<EditSession.QueryRunner> qr = new Mock<EditSession.QueryRunner>();
qr.Setup(r => r(It.IsAny<string>())).Throws(new Exception("qqq"));
// ... Create a mock for verifying the failure handler will be called
var successHandler = DoNothingSuccessMock;
var failureHandler = DoNothingFailureMock;
// If: I initialize the session with a query runner that will fail
s.Initialize(DoNothingConnector, qr.Object, successHandler.Object, failureHandler.Object);
await s.InitializeTask;
// Then:
// ... The session should not be initialized
Assert.False(s.IsInitialized);
// ... The failure handler should have been called once
failureHandler.Verify(f => f(It.IsAny<Exception>()), Times.Once);
// ... The success handler should not have been called at all
successHandler.Verify(f => f(), Times.Never);
}
[Theory]
[InlineData(null)]
[InlineData("It fail.")]
public async Task InitializeQueryFailReturnNull(string message)
{
// Setup:
// ... Create a metadata factory that will return some generic column information
var b = QueryExecution.Common.GetBasicExecutedBatch();
var etm = Common.GetStandardMetadata(b.ResultSets[0].Columns);
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
emf.Setup(f => f.GetObjectMetadata(It.IsAny<DbConnection>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(etm);
// ... Create a session that hasn't been initialized
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// ... Create a query runner that will fail via returning a null query
Mock<EditSession.QueryRunner> qr = new Mock<EditSession.QueryRunner>();
qr.Setup(r => r(It.IsAny<string>()))
.Returns(Task.FromResult(new EditSession.EditSessionQueryExecutionState(null, message)));
// ... Create a mock for verifying the failure handler will be called
var successHandler = DoNothingSuccessMock;
var failureHandler = DoNothingFailureMock;
// If: I initialize the session with a query runner that will fail
s.Initialize(DoNothingConnector, qr.Object, successHandler.Object, failureHandler.Object);
await s.InitializeTask;
// Then:
// ... The session should not be initialized
Assert.False(s.IsInitialized);
// ... The failure handler should have been called once
failureHandler.Verify(f => f(It.IsAny<Exception>()), Times.Once);
// ... The success handler should not have been called at all
successHandler.Verify(f => f(), Times.Never);
}
[Fact]
public async Task InitializeSuccess()
{
// Setup:
// ... Create a metadata factory that will return some generic column information
var q = QueryExecution.Common.GetBasicExecutedQuery();
var rs = q.Batches[0].ResultSets[0];
var etm = Common.GetStandardMetadata(rs.Columns);
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
emf.Setup(f => f.GetObjectMetadata(It.IsAny<DbConnection>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(etm);
// ... Create a session that hasn't been initialized
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// ... Create a query runner that will return a successful query
Mock<EditSession.QueryRunner> qr = new Mock<EditSession.QueryRunner>();
qr.Setup(r => r(It.IsAny<string>()))
.Returns(Task.FromResult(new EditSession.EditSessionQueryExecutionState(q, null)));
// ... Create a mock for verifying the failure handler will be called
var successHandler = DoNothingSuccessMock;
var failureHandler = DoNothingFailureMock;
// If: I initialize the session with a query runner that will fail
s.Initialize(DoNothingConnector, qr.Object, successHandler.Object, failureHandler.Object);
await s.InitializeTask;
// Then:
// ... The failure handler should not have been called
failureHandler.Verify(f => f(It.IsAny<Exception>()), Times.Never);
// ... The success handler should have been called
successHandler.Verify(f => f(), Times.Once);
// ... The session should have been initialized
Assert.True(s.IsInitialized);
Assert.Equal(rs.RowCount, s.NextRowId);
Assert.NotNull(s.EditCache);
Assert.Empty(s.EditCache);
}
#endregion
#region Delete Row Tests
[Fact]
public void DeleteRowAddFailure()
public void DeleteRowNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to delete a row without initializing
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.DeleteRow(0));
}
[Fact]
public async Task DeleteRowAddFailure()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a mock edit to the edit cache to cause the .TryAdd to fail
var mockEdit = new Mock<RowEditBase>().Object;
@@ -288,13 +547,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void DeleteRowSuccess()
public async Task DeleteRowSuccess()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
var s = await GetBasicSession();
// If: I add a row to the session
s.DeleteRow(0);
@@ -309,28 +565,24 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#region Revert Row Tests
[Fact]
public void RevertRowOutOfRange()
public void RevertRowNotInitialized()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I revert a row that doesn't have any pending changes
// If: I ask to revert a row without initializing
// Then: I should get an exception
Assert.Throws<ArgumentOutOfRangeException>(() => s.RevertRow(0));
Assert.Throws<InvalidOperationException>(() => s.RevertRow(0));
}
[Fact]
public void RevertRowSuccess()
public async Task RevertRowSuccess()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a mock edit to the edit cache to cause the .TryAdd to fail
var mockEdit = new Mock<RowEditBase>().Object;
@@ -346,17 +598,44 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#endregion
#region Revert Cell Tests
[Fact]
public void RevertCellNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to revert a cell without initializing
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.RevertCell(0, 0));
}
#endregion
#region Update Cell Tests
[Fact]
public void UpdateCellExisting()
public void UpdateCellNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to update a cell without initializing
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.UpdateCell(0, 0, ""));
}
[Fact]
public async Task UpdateCellExisting()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a mock edit to the edit cache to cause the .TryAdd to fail
var mockEdit = new Mock<RowEditBase>();
@@ -373,14 +652,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void UpdateCellNew()
public async Task UpdateCellNew()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// If: I update a cell on a row that does not have a pending edit
s.UpdateCell(0, 0, "");
@@ -395,14 +671,27 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#region SubSet Tests
[Fact]
public async Task SubsetNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to update a cell without initializing
// Then: I should get an exception
await Assert.ThrowsAsync<InvalidOperationException>(() => s.GetRows(0, 100));
}
[Fact]
public async Task GetRowsNoEdits()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
EditSession s = await Common.GetCustomSession(q, etm);
// If: I ask for 3 rows from session skipping the first
EditRow[] rows = await s.GetRows(1, 3);
@@ -439,10 +728,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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a cell update to it
s.UpdateCell(1, 0, "foo");
@@ -472,10 +758,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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a row deletion
s.DeleteRow(1);
@@ -505,10 +788,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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a row creation
s.CreateRow();
@@ -536,10 +816,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup:
// ... Create a session with a query and metadata
Query q = QueryExecution.Common.GetBasicExecutedQuery();
ResultSet rs = q.Batches[0].ResultSets[0];
IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add a few row creations
s.CreateRow();
@@ -561,17 +838,27 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#region Script Edits Tests
[Fact]
public void ScriptEditsNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to script edits without initializing
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.ScriptEdits(string.Empty));
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" \t\r\n")]
public void ScriptNullOrEmptyOutput(string outputPath)
public async Task ScriptNullOrEmptyOutput(string outputPath)
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// If: I try to script the edit cache with a null or whitespace output path
// Then: It should throw an exception
@@ -579,14 +866,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void ScriptProvidedOutputPath()
public async Task ScriptProvidedOutputPath()
{
// 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.GetStandardMetadata(rs.Columns);
EditSession s = new EditSession(rs, etm);
EditSession s = await GetBasicSession();
// ... Add two mock edits that will generate a script
Mock<RowEditBase> edit = new Mock<RowEditBase>();
@@ -613,10 +897,23 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#region Commit Tests
[Fact]
public void CommitNullConnection()
public void CommitEditsNotInitialized()
{
// Setup:
// ... Create a session without initializing
Mock<IEditMetadataFactory> emf = new Mock<IEditMetadataFactory>();
EditSession s = new EditSession(emf.Object, Constants.OwnerUri, Constants.OwnerUri);
// If: I ask to script edits without initializing
// Then: I should get an exception
Assert.Throws<InvalidOperationException>(() => s.CommitEdits(null, null, null));
}
[Fact]
public async Task CommitNullConnection()
{
// Setup: Create a basic session
EditSession s = GetBasicSession();
EditSession s = await GetBasicSession();
// If: I attempt to commit with a null connection
// Then: I should get an exception
@@ -625,11 +922,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void CommitNullSuccessHandler()
public async Task CommitNullSuccessHandler()
{
// Setup:
// ... Create a basic session
EditSession s = GetBasicSession();
EditSession s = await GetBasicSession();
// ... Mock db connection
DbConnection conn = new TestSqlConnection(null);
@@ -640,11 +937,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void CommitNullFailureHandler()
public async Task CommitNullFailureHandler()
{
// Setup:
// ... Create a basic session
EditSession s = GetBasicSession();
EditSession s = await GetBasicSession();
// ... Mock db connection
DbConnection conn = new TestSqlConnection(null);
@@ -655,11 +952,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
[Fact]
public void CommitInProgress()
public async Task CommitInProgress()
{
// Setup:
// ... Basic session and db connection
EditSession s = GetBasicSession();
EditSession s = await GetBasicSession();
DbConnection conn = new TestSqlConnection(null);
// ... Mock a task that has not completed
@@ -677,7 +974,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup:
// ... Basic session and db connection
EditSession s = GetBasicSession();
EditSession s = await GetBasicSession();
DbConnection conn = new TestSqlConnection(null);
// ... Add a mock commands for fun
@@ -721,7 +1018,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup:
// ... Basic session and db connection
EditSession s = GetBasicSession();
EditSession s = await GetBasicSession();
DbConnection conn = new TestSqlConnection(null);
// ... Add a mock edit that will explode on generating a command
@@ -760,12 +1057,51 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#endregion
private static EditSession GetBasicSession()
private static EditSession.Connector DoNothingConnector
{
get { return () => Task.FromResult<DbConnection>(null); }
}
private static EditSession.QueryRunner DoNothingQueryRunner
{
get { return q => Task.FromResult<EditSession.EditSessionQueryExecutionState>(null); }
}
private static Func<Task> DoNothingSuccessHandler
{
get { return () => Task.FromResult(0); }
}
private static Func<Exception, Task> DoNothingFailureHandler
{
get { return e => Task.FromResult(0); }
}
private static Mock<Func<Task>> DoNothingSuccessMock
{
get {
Mock<Func<Task>> successHandler = new Mock<Func<Task>>();
successHandler.Setup(f => f()).Returns(Task.FromResult(0));
return successHandler;
}
}
private static Mock<Func<Exception, Task>> DoNothingFailureMock
{
get
{
Mock<Func<Exception, Task>> failureHandler = new Mock<Func<Exception, Task>>();
failureHandler.Setup(f => f(It.IsAny<Exception>())).Returns(Task.FromResult(0));
return failureHandler;
}
}
private static async Task<EditSession> GetBasicSession()
{
Query q = QueryExecution.Common.GetBasicExecutedQuery();
ResultSet rs = q.Batches[0].ResultSets[0];
IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
return new EditSession(rs, etm);
EditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
return await Common.GetCustomSession(q, etm);
}
}
}

View File

@@ -302,7 +302,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
};
// Setup: Create a DATE column
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTe"));
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn {DataTypeName = "DaTe"});
foreach (DateTime value in testValues)
{
@@ -324,7 +324,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
};
// Setup: Create a DATETIME column
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe"));
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn {DataTypeName = "DaTeTiMe"});
foreach (DateTime value in testValues)
{
@@ -353,7 +353,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
};
// Setup: Create a DATETIME column
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe2", precision));
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn
{
DataTypeName = "DaTeTiMe2",
NumericScale = precision
});
foreach (DateTime value in testValues)
{
@@ -379,7 +383,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
};
// Setup: Create a DATETIME2 column
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe2", 0));
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn {DataTypeName = "DaTeTiMe2", NumericScale = 0});
foreach (DateTime value in testValues)
{
@@ -401,7 +405,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
};
// Setup: Create a DATETIME2 column
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn("col", "DaTeTiMe2", 255));
DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn {DataTypeName = "DaTeTiMe2", NumericScale = 255});
foreach (DateTime value in testValues)
{

View File

@@ -4,11 +4,85 @@
using System;
using System.Data.Common;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
{
public class TestDbColumn : DbColumn
{
#region Overridden Properties
public new bool AllowDBNull
{
get { return base.AllowDBNull.HasTrue(); }
set { base.AllowDBNull = value; }
}
public new string ColumnName
{
get { return base.ColumnName; }
set { base.ColumnName = value; }
}
public new int ColumnSize
{
get { return base.ColumnSize ?? -1; }
set { base.ColumnSize = value; }
}
public new Type DataType
{
get { return base.DataType; }
set { base.DataType = value; }
}
public new string DataTypeName
{
get { return base.DataTypeName; }
set { base.DataTypeName = value; }
}
public new bool IsAutoIncrement
{
get { return base.IsAutoIncrement.HasTrue(); }
set { base.IsAutoIncrement = value; }
}
public new bool IsLong
{
get { return base.IsLong.HasTrue(); }
set { base.IsLong = value; }
}
public new bool IsIdentity
{
get { return base.IsIdentity.HasTrue(); }
set { base.IsIdentity = value; }
}
public new bool IsKey
{
get { return base.IsKey.HasTrue(); }
set { base.IsKey = value; }
}
public new int NumericScale
{
get { return base.NumericScale ?? -1; }
set { base.NumericScale = value; }
}
#endregion
public TestDbColumn()
{
base.ColumnName = "col";
}
/// <summary>
/// Constructs a basic DbColumn that is an NVARCHAR(128) NULL
/// </summary>
/// <param name="columnName">Name of the column</param>
public TestDbColumn(string columnName)
{
base.IsLong = false;
@@ -18,34 +92,5 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
base.DataType = typeof(string);
base.DataTypeName = "nvarchar";
}
public TestDbColumn(string columnName, string columnType)
: this(columnName)
{
base.DataTypeName = columnType;
}
public TestDbColumn(string columnName, string columnType, Type columnDataType)
{
base.IsLong = false;
base.ColumnName = columnName;
base.ColumnSize = 128;
base.AllowDBNull = true;
base.DataType = columnDataType;
base.DataTypeName = columnType;
}
public TestDbColumn(string columnName, string columnType, int scale)
: this(columnName, columnType)
{
base.NumericScale = scale;
}
public TestDbColumn(string columnName, bool isAutoIncrement)
: this(columnName)
{
base.IsAutoIncrement = isAutoIncrement;
base.IsIdentity = isAutoIncrement;
}
}
}