From 52ac038ebe14c8617d2d253bd5b7f6e885426ce9 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Fri, 3 Mar 2017 15:47:47 -0800 Subject: [PATCH] edit/commit Command (#262) The main goal of this feature is to enable a command that will 1) Generate a parameterized command for each edit that is in the session 2) Execute that command against the server 3) Update the cached results of the table/view that's being edited with the committed changes (including computed/identity columns) There's some secret sauce in here where I cheated around worrying about gaps in the updated results. This was accomplished by implementing an IComparable for row edit objects that ensures deletes are the *last* actions to occur and that they occur from the bottom of the list up (highest row ID to lowest). Thus, all other actions that are dependent on the row ID are performed first, then the largest row ID is deleted, then next largest, etc. Nevertheless, by the end of a commit the associated ResultSet is still the source of truth. It is expected that the results grid will need updating once changes are committed. Also worth noting, although this pull request supports a "many edits, one commit" approach, it will work just fine for a "one edit, one commit" approach. * WIP * Adding basic commit support. Deletions work! * Nailing down the commit logic, insert commits work! * Updates work! * Fixing bug in DbColumnWrapper IsReadOnly setting * Comments * ResultSet unit tests, fixing issue with seeking in mock writers * Unit tests for RowCreate commands * Unit tests for RowDelete * RowUpdate unit tests * Session and edit base tests * Fixing broken unit tests * Moving constants to constants file * Addressing code review feedback * Fixes from merge issues, string consts * Removing ad-hoc code * fixing as per @abist requests * Fixing a couple more issues --- .../Localization/sr.Designer.cs | 848 +---------------- .../Localization/sr.cs | 52 +- .../Localization/sr.es.resx | 98 +- .../Localization/sr.resx | 12 +- .../Localization/sr.Designer.cs | 758 +-------------- .../Localization/sr.cs | 132 +-- .../Localization/sr.es.resx | 98 +- .../Localization/sr.resx | 32 +- .../Utility/LongList.cs | 29 + .../Utility/Validate.cs | 8 +- .../EditData/Contracts/EditCommitRequest.cs | 30 + .../EditData/EditDataService.cs | 67 +- .../EditData/{Session.cs => EditSession.cs} | 82 +- .../EditData/UpdateManagement/CellUpdate.cs | 6 +- .../EditData/UpdateManagement/RowCreate.cs | 137 ++- .../EditData/UpdateManagement/RowDelete.cs | 65 +- .../EditData/UpdateManagement/RowEdit.cs | 99 +- .../EditData/UpdateManagement/RowUpdate.cs | 102 ++- .../Localization/sr.cs | 862 +++++++++--------- .../Localization/sr.es.resx | 98 +- .../Localization/sr.resx | 218 ++--- .../Localization/sr.strings | 10 +- .../Localization/sr.xlf | 20 +- .../QueryExecution/Batch.cs | 4 +- .../Contracts/DbColumnWrapper.cs | 2 +- .../DataStorage/IFileStreamWriter.cs | 2 +- .../DataStorage/SaveAsWriterBase.cs | 9 + .../ServiceBufferFileStreamWriter.cs | 8 + .../QueryExecution/ResultSet.cs | 139 ++- .../Utility/SqlScriptFormatter.cs | 3 +- .../EditData/CellUpdateTests.cs | 52 +- .../EditData/Common.cs | 21 +- .../EditData/RowCreateTests.cs | 107 +++ .../EditData/RowDeleteTests.cs | 90 +- .../EditData/RowEditBaseTests.cs | 108 ++- .../EditData/RowUpdateTests.cs | 121 ++- .../EditData/ServiceIntegrationTests.cs | 12 +- .../EditData/SessionTests.cs | 200 +++- .../QueryExecution/DisposeTests.cs | 3 +- .../Execution/ResultSetTests.cs | 230 ++++- .../SaveResults/ResultSetTests.cs | 13 +- .../QueryExecution/SubsetTests.cs | 2 +- .../Utility/TestDbDataSet.cs | 7 +- .../Utility/TestObjects.cs | 14 + 44 files changed, 2546 insertions(+), 2464 deletions(-) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCommitRequest.cs rename src/Microsoft.SqlTools.ServiceLayer/EditData/{Session.cs => EditSession.cs} (69%) diff --git a/src/Microsoft.SqlTools.Credentials/Localization/sr.Designer.cs b/src/Microsoft.SqlTools.Credentials/Localization/sr.Designer.cs index 18805258..58451df3 100644 --- a/src/Microsoft.SqlTools.Credentials/Localization/sr.Designer.cs +++ b/src/Microsoft.SqlTools.Credentials/Localization/sr.Designer.cs @@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.Credentials.Localization { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.sr", typeof(sr).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.Credentials.Localization.sr", typeof(sr).GetTypeInfo().Assembly); resourceMan = temp; } return resourceMan; @@ -59,195 +59,6 @@ namespace Microsoft.SqlTools.Credentials.Localization { } } - /// - /// Looks up a localized string similar to File '{0}' recursively included.. - /// - public static string BatchParser_CircularReference { - get { - return ResourceManager.GetString("BatchParser_CircularReference", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Missing end comment mark '*/'.. - /// - public static string BatchParser_CommentNotTerminated { - get { - return ResourceManager.GetString("BatchParser_CommentNotTerminated", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Incorrect syntax was encountered while parsing '{0}'.. - /// - public static string BatchParser_IncorrectSyntax { - get { - return ResourceManager.GetString("BatchParser_IncorrectSyntax", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unclosed quotation mark after the character string.. - /// - public static string BatchParser_StringNotTerminated { - get { - return ResourceManager.GetString("BatchParser_StringNotTerminated", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Variable {0} is not defined.. - /// - public static string BatchParser_VariableNotDefined { - get { - return ResourceManager.GetString("BatchParser_VariableNotDefined", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Canceling batch parser wrapper batch execution.. - /// - public static string BatchParserWrapperExecutionEngineBatchCancelling { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchCancelling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}. - /// - public static string BatchParserWrapperExecutionEngineBatchMessage { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution engine batch ResultSet finished.. - /// - public static string BatchParserWrapperExecutionEngineBatchResultSetFinished { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchResultSetFinished", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}. - /// - public static string BatchParserWrapperExecutionEngineBatchResultSetProcessing { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchResultSetProcessing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SQL Execution error: {0}. - /// - public static string BatchParserWrapperExecutionEngineError { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}. - /// - public static string BatchParserWrapperExecutionError { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connection details object cannot be null. - /// - public static string ConnectionParamsValidateNullConnection { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullConnection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to OwnerUri cannot be null or empty. - /// - public static string ConnectionParamsValidateNullOwnerUri { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullOwnerUri", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ServerName cannot be null or empty. - /// - public static string ConnectionParamsValidateNullServerName { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullServerName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} cannot be null or empty when using SqlLogin authentication. - /// - public static string ConnectionParamsValidateNullSqlAuth { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullSqlAuth", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connection parameters cannot be null. - /// - public static string ConnectionServiceConnectErrorNullParams { - get { - return ResourceManager.GetString("ConnectionServiceConnectErrorNullParams", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connection canceled. - /// - public static string ConnectionServiceConnectionCanceled { - get { - return ResourceManager.GetString("ConnectionServiceConnectionCanceled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'.. - /// - public static string ConnectionServiceConnStringInvalidAuthType { - get { - return ResourceManager.GetString("ConnectionServiceConnStringInvalidAuthType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.. - /// - public static string ConnectionServiceConnStringInvalidIntent { - get { - return ResourceManager.GetString("ConnectionServiceConnStringInvalidIntent", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SpecifiedUri '{0}' does not have existing connection. - /// - public static string ConnectionServiceListDbErrorNotConnected { - get { - return ResourceManager.GetString("ConnectionServiceListDbErrorNotConnected", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to OwnerUri cannot be null or empty. - /// - public static string ConnectionServiceListDbErrorNullOwnerUri { - get { - return ResourceManager.GetString("ConnectionServiceListDbErrorNullOwnerUri", resourceCulture); - } - } - /// /// Looks up a localized string similar to Win32Credential object is already disposed. /// @@ -292,662 +103,5 @@ namespace Microsoft.SqlTools.Credentials.Localization { return ResourceManager.GetString("CredentialsServiceTargetForLookup", resourceCulture); } } - - /// - /// Looks up a localized string similar to An error occurred while the batch was being processed. The error message is: {0}. - /// - public static string EE_BatchError_Exception { - get { - return ResourceManager.GetString("EE_BatchError_Exception", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An error occurred while the batch was being executed.. - /// - public static string EE_BatchExecutionError_Halting { - get { - return ResourceManager.GetString("EE_BatchExecutionError_Halting", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An error occurred while the batch was being executed, but the error has been ignored.. - /// - public static string EE_BatchExecutionError_Ignoring { - get { - return ResourceManager.GetString("EE_BatchExecutionError_Ignoring", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ({0} row(s) affected). - /// - public static string EE_BatchExecutionInfo_RowsAffected { - get { - return ResourceManager.GetString("EE_BatchExecutionInfo_RowsAffected", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}. - /// - public static string EE_BatchSqlMessageNoLineInfo { - get { - return ResourceManager.GetString("EE_BatchSqlMessageNoLineInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Line {3}. - /// - public static string EE_BatchSqlMessageNoProcedureInfo { - get { - return ResourceManager.GetString("EE_BatchSqlMessageNoProcedureInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}. - /// - public static string EE_BatchSqlMessageWithProcedureInfo { - get { - return ResourceManager.GetString("EE_BatchSqlMessageWithProcedureInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Command {0} is not supported.. - /// - public static string EE_ExecutionError_CommandNotSupported { - get { - return ResourceManager.GetString("EE_ExecutionError_CommandNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable {0} could not be found.. - /// - public static string EE_ExecutionError_VariableNotFound { - get { - return ResourceManager.GetString("EE_ExecutionError_VariableNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Execution completed {0} times.... - /// - public static string EE_ExecutionInfo_FinalizingLoop { - get { - return ResourceManager.GetString("EE_ExecutionInfo_FinalizingLoop", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Starting execution loop of {0} times.... - /// - public static string EE_ExecutionInfo_InitilizingLoop { - get { - return ResourceManager.GetString("EE_ExecutionInfo_InitilizingLoop", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You cancelled the query.. - /// - public static string EE_ExecutionInfo_QueryCancelledbyUser { - get { - return ResourceManager.GetString("EE_ExecutionInfo_QueryCancelledbyUser", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The previous execution is not yet complete.. - /// - public static string EE_ExecutionNotYetCompleteError { - get { - return ResourceManager.GetString("EE_ExecutionNotYetCompleteError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A scripting error occurred.. - /// - public static string EE_ScriptError_Error { - get { - return ResourceManager.GetString("EE_ScriptError_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A fatal error occurred.. - /// - public static string EE_ScriptError_FatalError { - get { - return ResourceManager.GetString("EE_ScriptError_FatalError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Incorrect syntax was encountered while {0} was being parsed.. - /// - public static string EE_ScriptError_ParsingSyntax { - get { - return ResourceManager.GetString("EE_ScriptError_ParsingSyntax", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Scripting warning.. - /// - public static string EE_ScriptError_Warning { - get { - return ResourceManager.GetString("EE_ScriptError_Warning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replacement of an empty string by an empty string.. - /// - public static string ErrorEmptyStringReplacement { - get { - return ResourceManager.GetString("ErrorEmptyStringReplacement", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot convert SqlCodeObject Type {0} to Type {1}. - /// - public static string ErrorUnexpectedCodeObjectType { - get { - return ResourceManager.GetString("ErrorUnexpectedCodeObjectType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message header must separate key and value using ':'. - /// - public static string HostingHeaderMissingColon { - get { - return ResourceManager.GetString("HostingHeaderMissingColon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fatal error: Content-Length header must be provided. - /// - public static string HostingHeaderMissingContentLengthHeader { - get { - return ResourceManager.GetString("HostingHeaderMissingContentLengthHeader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fatal error: Content-Length value is not an integer. - /// - public static string HostingHeaderMissingContentLengthValue { - get { - return ResourceManager.GetString("HostingHeaderMissingContentLengthValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MessageReader's input stream ended unexpectedly, terminating. - /// - public static string HostingUnexpectedEndOfStream { - get { - return ResourceManager.GetString("HostingUnexpectedEndOfStream", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Service of type {0} cannot be created by ExtensionLoader<{1}>. - /// - public static string IncompatibleServiceForExtensionLoader { - get { - return ResourceManager.GetString("IncompatibleServiceForExtensionLoader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Multiple services found for type {0}, expected only 1. - /// - public static string MultipleServicesFound { - get { - return ResourceManager.GetString("MultipleServicesFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}. - /// - public static string PeekDefinitionAzureError { - get { - return ResourceManager.GetString("PeekDefinitionAzureError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No database object was retrieved.. - /// - public static string PeekDefinitionDatabaseError { - get { - return ResourceManager.GetString("PeekDefinitionDatabaseError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An unexpected error occurred during Peek Definition execution: {0}. - /// - public static string PeekDefinitionError { - get { - return ResourceManager.GetString("PeekDefinitionError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No results were found.. - /// - public static string PeekDefinitionNoResultsError { - get { - return ResourceManager.GetString("PeekDefinitionNoResultsError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Please connect to a server.. - /// - public static string PeekDefinitionNotConnectedError { - get { - return ResourceManager.GetString("PeekDefinitionNotConnectedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Operation timed out.. - /// - public static string PeekDefinitionTimedoutError { - get { - return ResourceManager.GetString("PeekDefinitionTimedoutError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This object type is currently not supported by this feature.. - /// - public static string PeekDefinitionTypeNotSupportedError { - get { - return ResourceManager.GetString("PeekDefinitionTypeNotSupportedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (1 row affected). - /// - public static string QueryServiceAffectedOneRow { - get { - return ResourceManager.GetString("QueryServiceAffectedOneRow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ({0} rows affected). - /// - public static string QueryServiceAffectedRows { - get { - return ResourceManager.GetString("QueryServiceAffectedRows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The query has already completed, it cannot be cancelled. - /// - public static string QueryServiceCancelAlreadyCompleted { - get { - return ResourceManager.GetString("QueryServiceCancelAlreadyCompleted", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Query successfully cancelled, failed to dispose query. Owner URI not found.. - /// - public static string QueryServiceCancelDisposeFailed { - get { - return ResourceManager.GetString("QueryServiceCancelDisposeFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (No column name). - /// - public static string QueryServiceColumnNull { - get { - return ResourceManager.GetString("QueryServiceColumnNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Commands completed successfully.. - /// - public static string QueryServiceCompletedSuccessfully { - get { - return ResourceManager.GetString("QueryServiceCompletedSuccessfully", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of bytes to return must be greater than zero. - /// - public static string QueryServiceDataReaderByteCountInvalid { - get { - return ResourceManager.GetString("QueryServiceDataReaderByteCountInvalid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of chars to return must be greater than zero. - /// - public static string QueryServiceDataReaderCharCountInvalid { - get { - return ResourceManager.GetString("QueryServiceDataReaderCharCountInvalid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of XML bytes to return must be greater than zero. - /// - public static string QueryServiceDataReaderXmlCountInvalid { - get { - return ResourceManager.GetString("QueryServiceDataReaderXmlCountInvalid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Line {3}{4}{5}. - /// - public static string QueryServiceErrorFormat { - get { - return ResourceManager.GetString("QueryServiceErrorFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not retrieve an execution plan from the result set . - /// - public static string QueryServiceExecutionPlanNotFound { - get { - return ResourceManager.GetString("QueryServiceExecutionPlanNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to FileStreamWrapper must be initialized before performing operations. - /// - public static string QueryServiceFileWrapperNotInitialized { - get { - return ResourceManager.GetString("QueryServiceFileWrapperNotInitialized", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This FileStreamWrapper cannot be used for writing. - /// - public static string QueryServiceFileWrapperReadOnly { - get { - return ResourceManager.GetString("QueryServiceFileWrapperReadOnly", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Access method cannot be write-only. - /// - public static string QueryServiceFileWrapperWriteOnly { - get { - return ResourceManager.GetString("QueryServiceFileWrapperWriteOnly", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sender for OnInfoMessage event must be a SqlConnection. - /// - public static string QueryServiceMessageSenderNotSql { - get { - return ResourceManager.GetString("QueryServiceMessageSenderNotSql", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Query was canceled by user. - /// - public static string QueryServiceQueryCancelled { - get { - return ResourceManager.GetString("QueryServiceQueryCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Query failed: {0}. - /// - public static string QueryServiceQueryFailed { - get { - return ResourceManager.GetString("QueryServiceQueryFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A query is already in progress for this editor session. Please cancel this query or wait for its completion.. - /// - public static string QueryServiceQueryInProgress { - get { - return ResourceManager.GetString("QueryServiceQueryInProgress", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This editor is not connected to a database. - /// - public static string QueryServiceQueryInvalidOwnerUri { - get { - return ResourceManager.GetString("QueryServiceQueryInvalidOwnerUri", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The requested query does not exist. - /// - public static string QueryServiceRequestsNoQuery { - get { - return ResourceManager.GetString("QueryServiceRequestsNoQuery", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not retrieve column schema for result set. - /// - public static string QueryServiceResultSetNoColumnSchema { - get { - return ResourceManager.GetString("QueryServiceResultSetNoColumnSchema", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot read subset unless the results have been read from the server. - /// - public static string QueryServiceResultSetNotRead { - get { - return ResourceManager.GetString("QueryServiceResultSetNotRead", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reader cannot be null. - /// - public static string QueryServiceResultSetReaderNull { - get { - return ResourceManager.GetString("QueryServiceResultSetReaderNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Row count must be a positive integer. - /// - public static string QueryServiceResultSetRowCountOutOfRange { - get { - return ResourceManager.GetString("QueryServiceResultSetRowCountOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Start row cannot be less than 0 or greater than the number of rows in the result set. - /// - public static string QueryServiceResultSetStartRowOutOfRange { - get { - return ResourceManager.GetString("QueryServiceResultSetStartRowOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to save {0}: {1}. - /// - public static string QueryServiceSaveAsFail { - get { - return ResourceManager.GetString("QueryServiceSaveAsFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A save request to the same path is in progress. - /// - public static string QueryServiceSaveAsInProgress { - get { - return ResourceManager.GetString("QueryServiceSaveAsInProgress", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Internal error occurred while starting save task. - /// - public static string QueryServiceSaveAsMiscStartingError { - get { - return ResourceManager.GetString("QueryServiceSaveAsMiscStartingError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Result cannot be saved until query execution has completed. - /// - public static string QueryServiceSaveAsResultSetNotComplete { - get { - return ResourceManager.GetString("QueryServiceSaveAsResultSetNotComplete", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The batch has not completed, yet. - /// - public static string QueryServiceSubsetBatchNotCompleted { - get { - return ResourceManager.GetString("QueryServiceSubsetBatchNotCompleted", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch index cannot be less than 0 or greater than the number of batches. - /// - public static string QueryServiceSubsetBatchOutOfRange { - get { - return ResourceManager.GetString("QueryServiceSubsetBatchOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Result set index cannot be less than 0 or greater than the number of result sets. - /// - public static string QueryServiceSubsetResultSetOutOfRange { - get { - return ResourceManager.GetString("QueryServiceSubsetResultSetOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot register service for type {0}, one or more services already registered. - /// - public static string ServiceAlreadyRegistered { - get { - return ResourceManager.GetString("ServiceAlreadyRegistered", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Service {0} was not found in the service provider. - /// - public static string ServiceNotFound { - get { - return ResourceManager.GetString("ServiceNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Service of Type {0} is not compatible with registered Type {1}. - /// - public static string ServiceNotOfExpectedType { - get { - return ResourceManager.GetString("ServiceNotOfExpectedType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SetServiceProvider() was not called to establish the required service provider. - /// - public static string ServiceProviderNotSet { - get { - return ResourceManager.GetString("ServiceProviderNotSet", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to EN_LOCALIZATION. - /// - public static string TestLocalizationConstant { - get { - return ResourceManager.GetString("TestLocalizationConstant", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to For more information about this error, see the troubleshooting topics in the product documentation.. - /// - public static string TroubleshootingAssistanceMessage { - get { - return ResourceManager.GetString("TroubleshootingAssistanceMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3}). - /// - public static string WorkspaceServiceBufferPositionOutOfOrder { - get { - return ResourceManager.GetString("WorkspaceServiceBufferPositionOutOfOrder", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Position is outside of column range for line {0}. - /// - public static string WorkspaceServicePositionColumnOutOfRange { - get { - return ResourceManager.GetString("WorkspaceServicePositionColumnOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Position is outside of file line range. - /// - public static string WorkspaceServicePositionLineOutOfRange { - get { - return ResourceManager.GetString("WorkspaceServicePositionLineOutOfRange", resourceCulture); - } - } } } diff --git a/src/Microsoft.SqlTools.Credentials/Localization/sr.cs b/src/Microsoft.SqlTools.Credentials/Localization/sr.cs index 6dd123a5..9f0625e1 100755 --- a/src/Microsoft.SqlTools.Credentials/Localization/sr.cs +++ b/src/Microsoft.SqlTools.Credentials/Localization/sr.cs @@ -27,7 +27,7 @@ namespace Microsoft.SqlTools.Credentials Keys.Culture = value; } } - + public static string CredentialsServiceInvalidCriticalHandle { @@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.Credentials { return Keys.GetString(Keys.CredentialsServiceInvalidCriticalHandle); } - } + } public static string CredentialsServicePasswordLengthExceeded { @@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.Credentials { return Keys.GetString(Keys.CredentialsServicePasswordLengthExceeded); } - } + } public static string CredentialsServiceTargetForDelete { @@ -51,7 +51,7 @@ namespace Microsoft.SqlTools.Credentials { return Keys.GetString(Keys.CredentialsServiceTargetForDelete); } - } + } public static string CredentialsServiceTargetForLookup { @@ -59,7 +59,7 @@ namespace Microsoft.SqlTools.Credentials { return Keys.GetString(Keys.CredentialsServiceTargetForLookup); } - } + } public static string CredentialServiceWin32CredentialDisposed { @@ -67,7 +67,7 @@ namespace Microsoft.SqlTools.Credentials { return Keys.GetString(Keys.CredentialServiceWin32CredentialDisposed); } - } + } [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Keys @@ -75,22 +75,22 @@ namespace Microsoft.SqlTools.Credentials static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.Credentials.Localization.SR", typeof(SR).GetTypeInfo().Assembly); static CultureInfo _culture = null; - - - public const string CredentialsServiceInvalidCriticalHandle = "CredentialsServiceInvalidCriticalHandle"; - - - public const string CredentialsServicePasswordLengthExceeded = "CredentialsServicePasswordLengthExceeded"; - - - public const string CredentialsServiceTargetForDelete = "CredentialsServiceTargetForDelete"; - - - public const string CredentialsServiceTargetForLookup = "CredentialsServiceTargetForLookup"; - - - public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed"; - + + + public const string CredentialsServiceInvalidCriticalHandle = "CredentialsServiceInvalidCriticalHandle"; + + + public const string CredentialsServicePasswordLengthExceeded = "CredentialsServicePasswordLengthExceeded"; + + + public const string CredentialsServiceTargetForDelete = "CredentialsServiceTargetForDelete"; + + + public const string CredentialsServiceTargetForLookup = "CredentialsServiceTargetForLookup"; + + + public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed"; + private Keys() { } @@ -111,7 +111,7 @@ namespace Microsoft.SqlTools.Credentials { return resourceManager.GetString(key, _culture); } - - } - } -} + + } + } +} diff --git a/src/Microsoft.SqlTools.Credentials/Localization/sr.es.resx b/src/Microsoft.SqlTools.Credentials/Localization/sr.es.resx index f43e5c5e..d65a6f96 100644 --- a/src/Microsoft.SqlTools.Credentials/Localization/sr.es.resx +++ b/src/Microsoft.SqlTools.Credentials/Localization/sr.es.resx @@ -1,18 +1,96 @@  + + + + + + + + + + + + + + + + + + + - + + @@ -27,5 +105,19 @@ -text/microsoft-resx1.3System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089ES_LOCALIZATION - \ No newline at end of file + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ES_LOCALIZATION + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.Credentials/Localization/sr.resx b/src/Microsoft.SqlTools.Credentials/Localization/sr.resx index b2ab23ff..b4947a92 100755 --- a/src/Microsoft.SqlTools.Credentials/Localization/sr.resx +++ b/src/Microsoft.SqlTools.Credentials/Localization/sr.resx @@ -120,21 +120,21 @@ Invalid CriticalHandle! - + The password has exceeded 512 bytes - + Target must be specified to delete a credential - + Target must be specified to check existance of a credential - + Win32Credential object is already disposed - - + + diff --git a/src/Microsoft.SqlTools.Hosting/Localization/sr.Designer.cs b/src/Microsoft.SqlTools.Hosting/Localization/sr.Designer.cs index 73730165..f10c6680 100644 --- a/src/Microsoft.SqlTools.Hosting/Localization/sr.Designer.cs +++ b/src/Microsoft.SqlTools.Hosting/Localization/sr.Designer.cs @@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.Hosting.Localization { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.sr", typeof(sr).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlTools.Hosting.Localization.sr", typeof(sr).GetTypeInfo().Assembly); resourceMan = temp; } return resourceMan; @@ -59,195 +59,6 @@ namespace Microsoft.SqlTools.Hosting.Localization { } } - /// - /// Looks up a localized string similar to File '{0}' recursively included.. - /// - public static string BatchParser_CircularReference { - get { - return ResourceManager.GetString("BatchParser_CircularReference", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Missing end comment mark '*/'.. - /// - public static string BatchParser_CommentNotTerminated { - get { - return ResourceManager.GetString("BatchParser_CommentNotTerminated", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Incorrect syntax was encountered while parsing '{0}'.. - /// - public static string BatchParser_IncorrectSyntax { - get { - return ResourceManager.GetString("BatchParser_IncorrectSyntax", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Unclosed quotation mark after the character string.. - /// - public static string BatchParser_StringNotTerminated { - get { - return ResourceManager.GetString("BatchParser_StringNotTerminated", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Variable {0} is not defined.. - /// - public static string BatchParser_VariableNotDefined { - get { - return ResourceManager.GetString("BatchParser_VariableNotDefined", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Canceling batch parser wrapper batch execution.. - /// - public static string BatchParserWrapperExecutionEngineBatchCancelling { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchCancelling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}. - /// - public static string BatchParserWrapperExecutionEngineBatchMessage { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution engine batch ResultSet finished.. - /// - public static string BatchParserWrapperExecutionEngineBatchResultSetFinished { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchResultSetFinished", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}. - /// - public static string BatchParserWrapperExecutionEngineBatchResultSetProcessing { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineBatchResultSetProcessing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SQL Execution error: {0}. - /// - public static string BatchParserWrapperExecutionEngineError { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionEngineError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}. - /// - public static string BatchParserWrapperExecutionError { - get { - return ResourceManager.GetString("BatchParserWrapperExecutionError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connection details object cannot be null. - /// - public static string ConnectionParamsValidateNullConnection { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullConnection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to OwnerUri cannot be null or empty. - /// - public static string ConnectionParamsValidateNullOwnerUri { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullOwnerUri", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ServerName cannot be null or empty. - /// - public static string ConnectionParamsValidateNullServerName { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullServerName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} cannot be null or empty when using SqlLogin authentication. - /// - public static string ConnectionParamsValidateNullSqlAuth { - get { - return ResourceManager.GetString("ConnectionParamsValidateNullSqlAuth", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connection parameters cannot be null. - /// - public static string ConnectionServiceConnectErrorNullParams { - get { - return ResourceManager.GetString("ConnectionServiceConnectErrorNullParams", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connection canceled. - /// - public static string ConnectionServiceConnectionCanceled { - get { - return ResourceManager.GetString("ConnectionServiceConnectionCanceled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'.. - /// - public static string ConnectionServiceConnStringInvalidAuthType { - get { - return ResourceManager.GetString("ConnectionServiceConnStringInvalidAuthType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.. - /// - public static string ConnectionServiceConnStringInvalidIntent { - get { - return ResourceManager.GetString("ConnectionServiceConnStringInvalidIntent", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SpecifiedUri '{0}' does not have existing connection. - /// - public static string ConnectionServiceListDbErrorNotConnected { - get { - return ResourceManager.GetString("ConnectionServiceListDbErrorNotConnected", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to OwnerUri cannot be null or empty. - /// - public static string ConnectionServiceListDbErrorNullOwnerUri { - get { - return ResourceManager.GetString("ConnectionServiceListDbErrorNullOwnerUri", resourceCulture); - } - } - /// /// Looks up a localized string similar to Win32Credential object is already disposed. /// @@ -293,177 +104,6 @@ namespace Microsoft.SqlTools.Hosting.Localization { } } - /// - /// Looks up a localized string similar to An error occurred while the batch was being processed. The error message is: {0}. - /// - public static string EE_BatchError_Exception { - get { - return ResourceManager.GetString("EE_BatchError_Exception", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An error occurred while the batch was being executed.. - /// - public static string EE_BatchExecutionError_Halting { - get { - return ResourceManager.GetString("EE_BatchExecutionError_Halting", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An error occurred while the batch was being executed, but the error has been ignored.. - /// - public static string EE_BatchExecutionError_Ignoring { - get { - return ResourceManager.GetString("EE_BatchExecutionError_Ignoring", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ({0} row(s) affected). - /// - public static string EE_BatchExecutionInfo_RowsAffected { - get { - return ResourceManager.GetString("EE_BatchExecutionInfo_RowsAffected", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}. - /// - public static string EE_BatchSqlMessageNoLineInfo { - get { - return ResourceManager.GetString("EE_BatchSqlMessageNoLineInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Line {3}. - /// - public static string EE_BatchSqlMessageNoProcedureInfo { - get { - return ResourceManager.GetString("EE_BatchSqlMessageNoProcedureInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}. - /// - public static string EE_BatchSqlMessageWithProcedureInfo { - get { - return ResourceManager.GetString("EE_BatchSqlMessageWithProcedureInfo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Command {0} is not supported.. - /// - public static string EE_ExecutionError_CommandNotSupported { - get { - return ResourceManager.GetString("EE_ExecutionError_CommandNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable {0} could not be found.. - /// - public static string EE_ExecutionError_VariableNotFound { - get { - return ResourceManager.GetString("EE_ExecutionError_VariableNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Execution completed {0} times.... - /// - public static string EE_ExecutionInfo_FinalizingLoop { - get { - return ResourceManager.GetString("EE_ExecutionInfo_FinalizingLoop", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Starting execution loop of {0} times.... - /// - public static string EE_ExecutionInfo_InitilizingLoop { - get { - return ResourceManager.GetString("EE_ExecutionInfo_InitilizingLoop", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You cancelled the query.. - /// - public static string EE_ExecutionInfo_QueryCancelledbyUser { - get { - return ResourceManager.GetString("EE_ExecutionInfo_QueryCancelledbyUser", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The previous execution is not yet complete.. - /// - public static string EE_ExecutionNotYetCompleteError { - get { - return ResourceManager.GetString("EE_ExecutionNotYetCompleteError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A scripting error occurred.. - /// - public static string EE_ScriptError_Error { - get { - return ResourceManager.GetString("EE_ScriptError_Error", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A fatal error occurred.. - /// - public static string EE_ScriptError_FatalError { - get { - return ResourceManager.GetString("EE_ScriptError_FatalError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Incorrect syntax was encountered while {0} was being parsed.. - /// - public static string EE_ScriptError_ParsingSyntax { - get { - return ResourceManager.GetString("EE_ScriptError_ParsingSyntax", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Scripting warning.. - /// - public static string EE_ScriptError_Warning { - get { - return ResourceManager.GetString("EE_ScriptError_Warning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Replacement of an empty string by an empty string.. - /// - public static string ErrorEmptyStringReplacement { - get { - return ResourceManager.GetString("ErrorEmptyStringReplacement", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot convert SqlCodeObject Type {0} to Type {1}. - /// - public static string ErrorUnexpectedCodeObjectType { - get { - return ResourceManager.GetString("ErrorUnexpectedCodeObjectType", resourceCulture); - } - } - /// /// Looks up a localized string similar to Message header must separate key and value using ':'. /// @@ -518,357 +158,6 @@ namespace Microsoft.SqlTools.Hosting.Localization { } } - /// - /// Looks up a localized string similar to This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}. - /// - public static string PeekDefinitionAzureError { - get { - return ResourceManager.GetString("PeekDefinitionAzureError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No database object was retrieved.. - /// - public static string PeekDefinitionDatabaseError { - get { - return ResourceManager.GetString("PeekDefinitionDatabaseError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An unexpected error occurred during Peek Definition execution: {0}. - /// - public static string PeekDefinitionError { - get { - return ResourceManager.GetString("PeekDefinitionError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No results were found.. - /// - public static string PeekDefinitionNoResultsError { - get { - return ResourceManager.GetString("PeekDefinitionNoResultsError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Please connect to a server.. - /// - public static string PeekDefinitionNotConnectedError { - get { - return ResourceManager.GetString("PeekDefinitionNotConnectedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Operation timed out.. - /// - public static string PeekDefinitionTimedoutError { - get { - return ResourceManager.GetString("PeekDefinitionTimedoutError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This object type is currently not supported by this feature.. - /// - public static string PeekDefinitionTypeNotSupportedError { - get { - return ResourceManager.GetString("PeekDefinitionTypeNotSupportedError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (1 row affected). - /// - public static string QueryServiceAffectedOneRow { - get { - return ResourceManager.GetString("QueryServiceAffectedOneRow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ({0} rows affected). - /// - public static string QueryServiceAffectedRows { - get { - return ResourceManager.GetString("QueryServiceAffectedRows", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The query has already completed, it cannot be cancelled. - /// - public static string QueryServiceCancelAlreadyCompleted { - get { - return ResourceManager.GetString("QueryServiceCancelAlreadyCompleted", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Query successfully cancelled, failed to dispose query. Owner URI not found.. - /// - public static string QueryServiceCancelDisposeFailed { - get { - return ResourceManager.GetString("QueryServiceCancelDisposeFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (No column name). - /// - public static string QueryServiceColumnNull { - get { - return ResourceManager.GetString("QueryServiceColumnNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Commands completed successfully.. - /// - public static string QueryServiceCompletedSuccessfully { - get { - return ResourceManager.GetString("QueryServiceCompletedSuccessfully", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of bytes to return must be greater than zero. - /// - public static string QueryServiceDataReaderByteCountInvalid { - get { - return ResourceManager.GetString("QueryServiceDataReaderByteCountInvalid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of chars to return must be greater than zero. - /// - public static string QueryServiceDataReaderCharCountInvalid { - get { - return ResourceManager.GetString("QueryServiceDataReaderCharCountInvalid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maximum number of XML bytes to return must be greater than zero. - /// - public static string QueryServiceDataReaderXmlCountInvalid { - get { - return ResourceManager.GetString("QueryServiceDataReaderXmlCountInvalid", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Msg {0}, Level {1}, State {2}, Line {3}{4}{5}. - /// - public static string QueryServiceErrorFormat { - get { - return ResourceManager.GetString("QueryServiceErrorFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not retrieve an execution plan from the result set . - /// - public static string QueryServiceExecutionPlanNotFound { - get { - return ResourceManager.GetString("QueryServiceExecutionPlanNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to FileStreamWrapper must be initialized before performing operations. - /// - public static string QueryServiceFileWrapperNotInitialized { - get { - return ResourceManager.GetString("QueryServiceFileWrapperNotInitialized", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This FileStreamWrapper cannot be used for writing. - /// - public static string QueryServiceFileWrapperReadOnly { - get { - return ResourceManager.GetString("QueryServiceFileWrapperReadOnly", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Access method cannot be write-only. - /// - public static string QueryServiceFileWrapperWriteOnly { - get { - return ResourceManager.GetString("QueryServiceFileWrapperWriteOnly", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sender for OnInfoMessage event must be a SqlConnection. - /// - public static string QueryServiceMessageSenderNotSql { - get { - return ResourceManager.GetString("QueryServiceMessageSenderNotSql", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Query was canceled by user. - /// - public static string QueryServiceQueryCancelled { - get { - return ResourceManager.GetString("QueryServiceQueryCancelled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Query failed: {0}. - /// - public static string QueryServiceQueryFailed { - get { - return ResourceManager.GetString("QueryServiceQueryFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A query is already in progress for this editor session. Please cancel this query or wait for its completion.. - /// - public static string QueryServiceQueryInProgress { - get { - return ResourceManager.GetString("QueryServiceQueryInProgress", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This editor is not connected to a database. - /// - public static string QueryServiceQueryInvalidOwnerUri { - get { - return ResourceManager.GetString("QueryServiceQueryInvalidOwnerUri", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The requested query does not exist. - /// - public static string QueryServiceRequestsNoQuery { - get { - return ResourceManager.GetString("QueryServiceRequestsNoQuery", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not retrieve column schema for result set. - /// - public static string QueryServiceResultSetNoColumnSchema { - get { - return ResourceManager.GetString("QueryServiceResultSetNoColumnSchema", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot read subset unless the results have been read from the server. - /// - public static string QueryServiceResultSetNotRead { - get { - return ResourceManager.GetString("QueryServiceResultSetNotRead", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reader cannot be null. - /// - public static string QueryServiceResultSetReaderNull { - get { - return ResourceManager.GetString("QueryServiceResultSetReaderNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Row count must be a positive integer. - /// - public static string QueryServiceResultSetRowCountOutOfRange { - get { - return ResourceManager.GetString("QueryServiceResultSetRowCountOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Start row cannot be less than 0 or greater than the number of rows in the result set. - /// - public static string QueryServiceResultSetStartRowOutOfRange { - get { - return ResourceManager.GetString("QueryServiceResultSetStartRowOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to save {0}: {1}. - /// - public static string QueryServiceSaveAsFail { - get { - return ResourceManager.GetString("QueryServiceSaveAsFail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A save request to the same path is in progress. - /// - public static string QueryServiceSaveAsInProgress { - get { - return ResourceManager.GetString("QueryServiceSaveAsInProgress", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Internal error occurred while starting save task. - /// - public static string QueryServiceSaveAsMiscStartingError { - get { - return ResourceManager.GetString("QueryServiceSaveAsMiscStartingError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Result cannot be saved until query execution has completed. - /// - public static string QueryServiceSaveAsResultSetNotComplete { - get { - return ResourceManager.GetString("QueryServiceSaveAsResultSetNotComplete", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The batch has not completed, yet. - /// - public static string QueryServiceSubsetBatchNotCompleted { - get { - return ResourceManager.GetString("QueryServiceSubsetBatchNotCompleted", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Batch index cannot be less than 0 or greater than the number of batches. - /// - public static string QueryServiceSubsetBatchOutOfRange { - get { - return ResourceManager.GetString("QueryServiceSubsetBatchOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Result set index cannot be less than 0 or greater than the number of result sets. - /// - public static string QueryServiceSubsetResultSetOutOfRange { - get { - return ResourceManager.GetString("QueryServiceSubsetResultSetOutOfRange", resourceCulture); - } - } - /// /// Looks up a localized string similar to Cannot register service for type {0}, one or more services already registered. /// @@ -904,50 +193,5 @@ namespace Microsoft.SqlTools.Hosting.Localization { return ResourceManager.GetString("ServiceProviderNotSet", resourceCulture); } } - - /// - /// Looks up a localized string similar to EN_LOCALIZATION. - /// - public static string TestLocalizationConstant { - get { - return ResourceManager.GetString("TestLocalizationConstant", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to For more information about this error, see the troubleshooting topics in the product documentation.. - /// - public static string TroubleshootingAssistanceMessage { - get { - return ResourceManager.GetString("TroubleshootingAssistanceMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3}). - /// - public static string WorkspaceServiceBufferPositionOutOfOrder { - get { - return ResourceManager.GetString("WorkspaceServiceBufferPositionOutOfOrder", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Position is outside of column range for line {0}. - /// - public static string WorkspaceServicePositionColumnOutOfRange { - get { - return ResourceManager.GetString("WorkspaceServicePositionColumnOutOfRange", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Position is outside of file line range. - /// - public static string WorkspaceServicePositionLineOutOfRange { - get { - return ResourceManager.GetString("WorkspaceServicePositionLineOutOfRange", resourceCulture); - } - } } } diff --git a/src/Microsoft.SqlTools.Hosting/Localization/sr.cs b/src/Microsoft.SqlTools.Hosting/Localization/sr.cs index 7ec9a964..52ae9e27 100755 --- a/src/Microsoft.SqlTools.Hosting/Localization/sr.cs +++ b/src/Microsoft.SqlTools.Hosting/Localization/sr.cs @@ -27,7 +27,7 @@ namespace Microsoft.SqlTools.Hosting Keys.Culture = value; } } - + public static string CredentialsServiceInvalidCriticalHandle { @@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.CredentialsServiceInvalidCriticalHandle); } - } + } public static string CredentialsServicePasswordLengthExceeded { @@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.CredentialsServicePasswordLengthExceeded); } - } + } public static string CredentialsServiceTargetForDelete { @@ -51,7 +51,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.CredentialsServiceTargetForDelete); } - } + } public static string CredentialsServiceTargetForLookup { @@ -59,7 +59,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.CredentialsServiceTargetForLookup); } - } + } public static string CredentialServiceWin32CredentialDisposed { @@ -67,7 +67,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.CredentialServiceWin32CredentialDisposed); } - } + } public static string ServiceAlreadyRegistered { @@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.ServiceAlreadyRegistered); } - } + } public static string MultipleServicesFound { @@ -83,7 +83,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.MultipleServicesFound); } - } + } public static string IncompatibleServiceForExtensionLoader { @@ -91,7 +91,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.IncompatibleServiceForExtensionLoader); } - } + } public static string ServiceProviderNotSet { @@ -99,7 +99,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.ServiceProviderNotSet); } - } + } public static string ServiceNotFound { @@ -107,7 +107,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.ServiceNotFound); } - } + } public static string ServiceNotOfExpectedType { @@ -115,7 +115,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.ServiceNotOfExpectedType); } - } + } public static string HostingUnexpectedEndOfStream { @@ -123,7 +123,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.HostingUnexpectedEndOfStream); } - } + } public static string HostingHeaderMissingColon { @@ -131,7 +131,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.HostingHeaderMissingColon); } - } + } public static string HostingHeaderMissingContentLengthHeader { @@ -139,7 +139,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.HostingHeaderMissingContentLengthHeader); } - } + } public static string HostingHeaderMissingContentLengthValue { @@ -147,7 +147,7 @@ namespace Microsoft.SqlTools.Hosting { return Keys.GetString(Keys.HostingHeaderMissingContentLengthValue); } - } + } [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Keys @@ -155,52 +155,52 @@ namespace Microsoft.SqlTools.Hosting static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.Hosting.Localization.SR", typeof(SR).GetTypeInfo().Assembly); static CultureInfo _culture = null; - - - public const string CredentialsServiceInvalidCriticalHandle = "CredentialsServiceInvalidCriticalHandle"; - - - public const string CredentialsServicePasswordLengthExceeded = "CredentialsServicePasswordLengthExceeded"; - - - public const string CredentialsServiceTargetForDelete = "CredentialsServiceTargetForDelete"; - - - public const string CredentialsServiceTargetForLookup = "CredentialsServiceTargetForLookup"; - - - public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed"; - - - public const string ServiceAlreadyRegistered = "ServiceAlreadyRegistered"; - - - public const string MultipleServicesFound = "MultipleServicesFound"; - - - public const string IncompatibleServiceForExtensionLoader = "IncompatibleServiceForExtensionLoader"; - - - public const string ServiceProviderNotSet = "ServiceProviderNotSet"; - - - public const string ServiceNotFound = "ServiceNotFound"; - - - public const string ServiceNotOfExpectedType = "ServiceNotOfExpectedType"; - - - public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream"; - - - public const string HostingHeaderMissingColon = "HostingHeaderMissingColon"; - - - public const string HostingHeaderMissingContentLengthHeader = "HostingHeaderMissingContentLengthHeader"; - - - public const string HostingHeaderMissingContentLengthValue = "HostingHeaderMissingContentLengthValue"; - + + + public const string CredentialsServiceInvalidCriticalHandle = "CredentialsServiceInvalidCriticalHandle"; + + + public const string CredentialsServicePasswordLengthExceeded = "CredentialsServicePasswordLengthExceeded"; + + + public const string CredentialsServiceTargetForDelete = "CredentialsServiceTargetForDelete"; + + + public const string CredentialsServiceTargetForLookup = "CredentialsServiceTargetForLookup"; + + + public const string CredentialServiceWin32CredentialDisposed = "CredentialServiceWin32CredentialDisposed"; + + + public const string ServiceAlreadyRegistered = "ServiceAlreadyRegistered"; + + + public const string MultipleServicesFound = "MultipleServicesFound"; + + + public const string IncompatibleServiceForExtensionLoader = "IncompatibleServiceForExtensionLoader"; + + + public const string ServiceProviderNotSet = "ServiceProviderNotSet"; + + + public const string ServiceNotFound = "ServiceNotFound"; + + + public const string ServiceNotOfExpectedType = "ServiceNotOfExpectedType"; + + + public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream"; + + + public const string HostingHeaderMissingColon = "HostingHeaderMissingColon"; + + + public const string HostingHeaderMissingContentLengthHeader = "HostingHeaderMissingContentLengthHeader"; + + + public const string HostingHeaderMissingContentLengthValue = "HostingHeaderMissingContentLengthValue"; + private Keys() { } @@ -221,7 +221,7 @@ namespace Microsoft.SqlTools.Hosting { return resourceManager.GetString(key, _culture); } - - } - } -} + + } + } +} diff --git a/src/Microsoft.SqlTools.Hosting/Localization/sr.es.resx b/src/Microsoft.SqlTools.Hosting/Localization/sr.es.resx index f43e5c5e..d65a6f96 100644 --- a/src/Microsoft.SqlTools.Hosting/Localization/sr.es.resx +++ b/src/Microsoft.SqlTools.Hosting/Localization/sr.es.resx @@ -1,18 +1,96 @@  + + + + + + + + + + + + + + + + + + + - + + @@ -27,5 +105,19 @@ -text/microsoft-resx1.3System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089ES_LOCALIZATION - \ No newline at end of file + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ES_LOCALIZATION + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.Hosting/Localization/sr.resx b/src/Microsoft.SqlTools.Hosting/Localization/sr.resx index babafb52..532f5e47 100755 --- a/src/Microsoft.SqlTools.Hosting/Localization/sr.resx +++ b/src/Microsoft.SqlTools.Hosting/Localization/sr.resx @@ -120,61 +120,61 @@ Invalid CriticalHandle! - + The password has exceeded 512 bytes - + Target must be specified to delete a credential - + Target must be specified to check existance of a credential - + Win32Credential object is already disposed - + Cannot register service for type {0}, one or more services already registered - + Multiple services found for type {0}, expected only 1 - + Service of type {0} cannot be created by ExtensionLoader<{1}> - + SetServiceProvider() was not called to establish the required service provider - + Service {0} was not found in the service provider - + Service of Type {0} is not compatible with registered Type {1} - + MessageReader's input stream ended unexpectedly, terminating - + Message header must separate key and value using ':' - + Fatal error: Content-Length header must be provided - + Fatal error: Content-Length value is not an integer - - + + diff --git a/src/Microsoft.SqlTools.Hosting/Utility/LongList.cs b/src/Microsoft.SqlTools.Hosting/Utility/LongList.cs index f1cc7ad8..d3d0cc10 100644 --- a/src/Microsoft.SqlTools.Hosting/Utility/LongList.cs +++ b/src/Microsoft.SqlTools.Hosting/Utility/LongList.cs @@ -51,6 +51,11 @@ namespace Microsoft.SqlTools.Utility { return GetItem(index); } + + set + { + SetItem(index, value); + } } public int ExpandListSize @@ -137,6 +142,30 @@ namespace Microsoft.SqlTools.Utility return val; } + /// + /// Sets the item at the specified index + /// + /// Index of the item to set + /// The item to store at the index specified + public void SetItem(long index, T value) + { + Validate.IsLessThan(nameof(index), index, Count); + + if (Count <= this.ExpandListSize) + { + int i32Index = Convert.ToInt32(index); + shortList[i32Index] = value; + } + else + { + int iArray32Index = (int) (Count / this.ExpandListSize); + List arr = expandedList[iArray32Index]; + + int i32Index = (int)(Count % this.ExpandListSize); + arr[i32Index] = value; + } + } + /// /// Removes an item at the specified location and shifts all the items after the provided /// index up by one. diff --git a/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs b/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs index 39f2dd74..2f25bf6d 100644 --- a/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs +++ b/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs @@ -64,8 +64,8 @@ namespace Microsoft.SqlTools.Utility /// The upper limit which the value should be less than. public static void IsLessThan( string parameterName, - int valueToCheck, - int upperLimit) + long valueToCheck, + long upperLimit) { if (valueToCheck >= upperLimit) { @@ -87,8 +87,8 @@ namespace Microsoft.SqlTools.Utility /// The lower limit which the value should be greater than. public static void IsGreaterThan( string parameterName, - int valueToCheck, - int lowerLimit) + long valueToCheck, + long lowerLimit) { if (valueToCheck < lowerLimit) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCommitRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCommitRequest.cs new file mode 100644 index 00000000..8197e54c --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditCommitRequest.cs @@ -0,0 +1,30 @@ +// +// 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.Hosting.Protocol.Contracts; + +namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts +{ + /// + /// Parameters for a request to commit pending edit operations + /// + public class EditCommitParams : SessionOperationParams + { + } + + /// + /// Parameters to return upon successful completion of commiting pending edit operations + /// + public class EditCommitResult + { + } + + public class EditCommitRequest + { + public static readonly + RequestType Type = + RequestType.Create("edit/commit"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs index d42c6b49..09ca5119 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs @@ -54,8 +54,8 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData private readonly QueryExecutionService queryExecutionService; - private readonly Lazy> editSessions = new Lazy>( - () => new ConcurrentDictionary()); + private readonly Lazy> editSessions = new Lazy>( + () => new ConcurrentDictionary()); #endregion @@ -64,7 +64,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// Dictionary mapping OwnerURIs to active sessions /// - internal ConcurrentDictionary ActiveSessions => editSessions.Value; + internal ConcurrentDictionary ActiveSessions => editSessions.Value; #endregion @@ -86,14 +86,14 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData #region Request Handlers internal async Task HandleSessionRequest(SessionOperationParams sessionParams, - RequestContext requestContext, Func sessionOperation) + RequestContext requestContext, Func sessionOperation) { try { - Session session = GetActiveSessionOrThrow(sessionParams.OwnerUri); + EditSession editSession = GetActiveSessionOrThrow(sessionParams.OwnerUri); - // Get the result from execution of the session operation - TResult result = sessionOperation(session); + // Get the result from execution of the editSession operation + TResult result = sessionOperation(editSession); await requestContext.SendResult(result); } catch (Exception e) @@ -135,9 +135,9 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData // Sanity check the owner URI Validate.IsNotNullOrWhitespaceString(nameof(disposeParams.OwnerUri), disposeParams.OwnerUri); - // Attempt to remove the session - Session session; - if (!ActiveSessions.TryRemove(disposeParams.OwnerUri, out session)) + // Attempt to remove the editSession + EditSession editSession; + if (!ActiveSessions.TryRemove(disposeParams.OwnerUri, out editSession)) { await requestContext.SendError(SR.EditDataSessionNotFound); return; @@ -219,6 +219,31 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData session => session.UpdateCell(updateParams.RowId, updateParams.ColumnId, updateParams.NewValue)); } + internal async Task HandleCommitRequest(EditCommitParams commitParams, + RequestContext requestContext) + { + // Setup a callback for if the edits have been successfully written to the db + Func successHandler = () => requestContext.SendResult(new EditCommitResult()); + + // Setup a callback for if the edits failed to be written to db + Func failureHandler = e => requestContext.SendError(e.Message); + + try + { + // Get the editSession + EditSession editSession = GetActiveSessionOrThrow(commitParams.OwnerUri); + + // Get a connection for doing the committing + DbConnection conn = await connectionService.GetOrOpenConnection(commitParams.OwnerUri, + ConnectionType.Edit); + editSession.CommitEdits(conn, successHandler, failureHandler); + } + catch (Exception e) + { + await failureHandler(e); + } + } + #endregion #region Private Helpers @@ -229,19 +254,19 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// If the edit session doesn't exist /// Owner URI for the edit session /// The edit session that corresponds to the owner URI - private Session GetActiveSessionOrThrow(string ownerUri) + private EditSession GetActiveSessionOrThrow(string ownerUri) { // Sanity check the owner URI is provided Validate.IsNotNullOrWhitespaceString(nameof(ownerUri), ownerUri); - // Attempt to get the session, throw if unable - Session session; - if (!ActiveSessions.TryGetValue(ownerUri, out session)) + // Attempt to get the editSession, throw if unable + EditSession editSession; + if (!ActiveSessions.TryGetValue(ownerUri, out editSession)) { throw new Exception(SR.EditDataSessionNotFound); } - return session; + return editSession; } private async Task QueryCompleteCallback(Query query, EditInitializeParams initParams, @@ -254,19 +279,19 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData try { - // Validate the query for a session - ResultSet resultSet = Session.ValidateQueryForSession(query); + // Validate the query for a editSession + ResultSet resultSet = EditSession.ValidateQueryForSession(query); // 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 session and add it to the sessions list - Session session = new Session(resultSet, metadata); - if (!ActiveSessions.TryAdd(initParams.OwnerUri, session)) + // 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 session, session already exists."); + throw new InvalidOperationException("Failed to create edit editSession, editSession already exists."); } readyParams.Success = true; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/Session.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs similarity index 69% rename from src/Microsoft.SqlTools.ServiceLayer/EditData/Session.cs rename to src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs index b5b1f346..d490eb07 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/Session.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs @@ -5,8 +5,10 @@ using System; using System.Collections.Concurrent; +using System.Data.Common; using System.IO; using System.Linq; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; using Microsoft.SqlTools.ServiceLayer.QueryExecution; @@ -18,22 +20,18 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// Represents an edit "session" bound to the results of a query, containing a cache of edits /// that are pending. Provides logic for performing edit operations. /// - public class Session + public class EditSession { - #region Member Variables - private readonly ResultSet associatedResultSet; private readonly IEditTableMetadata objectMetadata; - #endregion - /// /// Constructs a new edit session bound to the result set and metadat object provided /// /// The result set of the table to be edited /// Metadata provider for the table to be edited - public Session(ResultSet resultSet, IEditTableMetadata objMetadata) + public EditSession(ResultSet resultSet, IEditTableMetadata objMetadata) { Validate.IsNotNull(nameof(resultSet), resultSet); Validate.IsNotNull(nameof(objMetadata), objMetadata); @@ -47,6 +45,12 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData #region Properties + /// + /// The task that is running to commit the changes to the db + /// Internal for unit test purposes. + /// + internal Task CommitTask { get; set; } + /// /// The internal ID for the next row in the table. Internal for unit testing purposes only. /// @@ -55,7 +59,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData /// /// The cache of pending updates. Internal for unit test purposes only /// - internal ConcurrentDictionary EditCache { get;} + internal ConcurrentDictionary EditCache { get; } #endregion @@ -109,6 +113,29 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData return newRowId; } + /// + /// Commits the edits in the cache to the database and then to the associated result set of + /// this edit session. This is launched asynchronously. + /// + /// The connection to use for executing the query + /// Callback to perform when the commit process has finished + /// Callback to perform if the commit process has failed at some point + public void CommitEdits(DbConnection connection, Func successHandler, Func errorHandler) + { + Validate.IsNotNull(nameof(connection), connection); + Validate.IsNotNull(nameof(successHandler), successHandler); + Validate.IsNotNull(nameof(errorHandler), errorHandler); + + // Make sure that there isn't a commit task in progress + if (CommitTask != null && !CommitTask.IsCompleted) + { + throw new InvalidOperationException(SR.EditDataCommitInProgress); + } + + // Start up the commit process + CommitTask = CommitEditsInternal(connection, successHandler, errorHandler); + } + /// /// Creates a delete row update and adds it to the update cache /// @@ -149,6 +176,11 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData } } + /// + /// Generates a single script file with all the pending edits scripted. + /// + /// The path to output the script to + /// public string ScriptEdits(string outputPath) { // Validate the output path @@ -203,7 +235,10 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData // Attempt to get the row that is being edited, create a new update object if one // doesn't exist - RowEditBase editRow = EditCache.GetOrAdd(rowId, new RowUpdate(rowId, associatedResultSet, objectMetadata)); + // NOTE: This *must* be done as a lambda. RowUpdate creation requires that the row + // exist in the result set. We only want a new RowUpdate to be created if the edit + // doesn't already exist in the cache + RowEditBase editRow = EditCache.GetOrAdd(rowId, key => new RowUpdate(rowId, associatedResultSet, objectMetadata)); // Pass the call to the row update return editRow.SetCell(columnId, newValue); @@ -211,5 +246,36 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData #endregion + private async Task CommitEditsInternal(DbConnection connection, Func successHandler, Func errorHandler) + { + try + { + // @TODO: Add support for transactional commits + + // Trust the RowEdit to sort itself appropriately + var editOperations = EditCache.Values.ToList(); + editOperations.Sort(); + foreach (var editOperation in editOperations) + { + // Get the command from the edit operation and execute it + using (DbCommand editCommand = editOperation.GetCommand(connection)) + using (DbDataReader reader = await editCommand.ExecuteReaderAsync()) + { + // Apply the changes of the command to the result set + await editOperation.ApplyChanges(reader); + } + + // If we succeeded in applying the changes, then remove this from the cache + // @TODO: Prevent edit sessions from being modified while a commit is in progress + RowEditBase re; + EditCache.TryRemove(editOperation.RowId, out re); + } + await successHandler(); + } + catch (Exception e) + { + await errorHandler(e); + } + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs index f802ef09..823d3902 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs @@ -4,9 +4,9 @@ // using System; -using System.Data.Common; using System.Globalization; using System.Text.RegularExpressions; +using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement @@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// /// Column the cell will be under /// The string from the client to convert to an object - public CellUpdate(DbColumn column, string valueAsString) + public CellUpdate(DbColumnWrapper column, string valueAsString) { Validate.IsNotNull(nameof(column), column); Validate.IsNotNull(nameof(valueAsString), valueAsString); @@ -89,7 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// /// The column that the cell will be placed in /// - public DbColumn Column { get; } + public DbColumnWrapper Column { get; } /// /// The object representation of the cell provided by the client diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs index 7e6c47a8..51d72248 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs @@ -5,10 +5,16 @@ using System; using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; +using System.Linq; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.Utility; +using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement { @@ -17,7 +23,9 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// public sealed class RowCreate : RowEditBase { - private const string InsertStatement = "INSERT INTO {0}({1}) VALUES ({2})"; + private const string InsertStart = "INSERT INTO {0}({1})"; + private const string InsertCompleteScript = "{0} VALUES ({1})"; + private const string InsertCompleteOutput = "{0} OUTPUT {1} VALUES ({2})"; private readonly CellUpdate[] newCells; @@ -34,42 +42,121 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement } /// - /// Generates the INSERT INTO statement that will apply the row creation + /// Sort ID for a RowCreate object. Setting to 1 ensures that these are the first changes + /// to be committed /// - /// INSERT INTO statement - public override string GetScript() - { - List columnNames = new List(); - List columnValues = new List(); + protected override int SortId => 1; - // Build the column list and value list + #region Public Methods + + /// + /// Applies the changes to the associated result set after successfully executing the + /// change on the database + /// + /// + /// Reader returned from the execution of the command to insert a new row. Should contain + /// a single row that represents the newly added row. + /// + public override Task ApplyChanges(DbDataReader dataReader) + { + Validate.IsNotNull(nameof(dataReader), dataReader); + + return AssociatedResultSet.AddRow(dataReader); + } + + /// + /// Generates a command that can be executed to insert a new row -- and return the newly + /// inserted row. + /// + /// The connection the command should be associated with + /// Command to insert the new row + public override DbCommand GetCommand(DbConnection connection) + { + Validate.IsNotNull(nameof(connection), connection); + + // Process all the columns. Add the column to the output columns, add updateable + // columns to the input parameters + List outColumns = new List(); + List inColumns = new List(); + DbCommand command = connection.CreateCommand(); for (int i = 0; i < AssociatedResultSet.Columns.Length; i++) { DbColumnWrapper column = AssociatedResultSet.Columns[i]; CellUpdate cell = newCells[i]; - // If the column is not updatable, then skip it + // Add the column to the output + outColumns.Add($"inserted.{SqlScriptFormatter.FormatIdentifier(column.ColumnName)}"); + + // Skip columns that cannot be updated if (!column.IsUpdatable) { continue; } - // If the cell doesn't have a value, but is updatable, don't try to create the script + // If we're missing a cell, then we cannot continue if (cell == null) { throw new InvalidOperationException(SR.EditDataCreateScriptMissingValue); } - // Add the column and the data to their respective lists - columnNames.Add(SqlScriptFormatter.FormatIdentifier(column.ColumnName)); - columnValues.Add(SqlScriptFormatter.FormatValue(cell.Value, column)); + // Create a parameter for the value and add it to the command + // Add the parameterization to the list and add it to the command + string paramName = $"@Value{RowId}{i}"; + inColumns.Add(paramName); + SqlParameter param = new SqlParameter(paramName, cell.Column.SqlDbType) + { + Value = cell.Value + }; + command.Parameters.Add(param); } + string joinedInColumns = string.Join(", ", inColumns); + string joinedOutColumns = string.Join(", ", outColumns); + + // Get the start clause + string start = GetTableClause(); - // Put together the components of the statement - string joinedColumnNames = string.Join(", ", columnNames); - string joinedColumnValues = string.Join(", ", columnValues); - return string.Format(InsertStatement, AssociatedObjectMetadata.EscapedMultipartName, joinedColumnNames, - joinedColumnValues); + // Put the whole #! together + command.CommandText = string.Format(InsertCompleteOutput, start, joinedOutColumns, joinedInColumns); + command.CommandType = CommandType.Text; + + return command; + } + + /// + /// Generates the INSERT INTO statement that will apply the row creation + /// + /// INSERT INTO statement + public override string GetScript() + { + // Process all the cells, and generate the values + List values = new List(); + for (int i = 0; i < AssociatedResultSet.Columns.Length; i++) + { + DbColumnWrapper column = AssociatedResultSet.Columns[i]; + CellUpdate cell = newCells[i]; + + // Skip columns that cannot be updated + if (!column.IsUpdatable) + { + continue; + } + + // If we're missing a cell, then we cannot continue + if (cell == null) + { + throw new InvalidOperationException(SR.EditDataCreateScriptMissingValue); + } + + // Format the value and add it to the list + values.Add(SqlScriptFormatter.FormatValue(cell.Value, column)); + } + string joinedValues = string.Join(", ", values); + + // Get the start clause + string start = GetTableClause(); + + // Put the whole #! together + return string.Format(InsertCompleteScript, start, joinedValues); } /// @@ -99,5 +186,19 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement }; return eucr; } + + #endregion + + private string GetTableClause() + { + // Get all the columns that will be provided + var inColumns = from c in AssociatedResultSet.Columns + where c.IsUpdatable + select SqlScriptFormatter.FormatIdentifier(c.ColumnName); + + // Package it into a single INSERT statement starter + string inColumnsJoined = string.Join(", ", inColumns); + return string.Format(InsertStart, AssociatedObjectMetadata.EscapedMultipartName, inColumnsJoined); + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs index f585691d..43d89419 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs @@ -4,9 +4,12 @@ // using System; +using System.Data.Common; using System.Globalization; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution; +using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement { @@ -29,15 +32,53 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement { } + /// + /// Sort ID for a RowDelete object. Setting to 2 ensures that these are the LAST changes + /// to be committed + /// + protected override int SortId => 2; + + /// + /// Applies the changes to the associated result set after successfully executing the + /// change on the database + /// + /// + /// Reader returned from the execution of the command to insert a new row. Should NOT + /// contain any rows. + /// + public override Task ApplyChanges(DbDataReader dataReader) + { + // Take the result set and remove the row from it + AssociatedResultSet.RemoveRow(RowId); + return Task.FromResult(0); + } + + /// + /// Generates a command for deleting the selected row + /// + /// + public override DbCommand GetCommand(DbConnection connection) + { + Validate.IsNotNull(nameof(connection), connection); + + // Return a SqlCommand with formatted with the parameters from the where clause + WhereClause where = GetWhereClause(true); + string commandText = GetCommandText(where.CommandText); + + DbCommand command = connection.CreateCommand(); + command.CommandText = commandText; + command.Parameters.AddRange(where.Parameters.ToArray()); + + return command; + } + /// /// Generates a DELETE statement to delete this row /// /// String of the DELETE statement public override string GetScript() { - string formatString = AssociatedObjectMetadata.IsMemoryOptimized ? DeleteMemoryOptimizedStatement : DeleteStatement; - return string.Format(CultureInfo.InvariantCulture, formatString, - AssociatedObjectMetadata.EscapedMultipartName, GetWhereClause(false).CommandText); + return GetCommandText(GetWhereClause(false).CommandText); } /// @@ -51,5 +92,23 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement { throw new InvalidOperationException(SR.EditDataDeleteSetCell); } + + protected override int CompareToSameType(RowEditBase rowEdit) + { + // We want to sort by row ID *IN REVERSE* to make sure we delete from the bottom first. + // If we delete from the top first, it will change IDs, making all subsequent deletes + // off by one or more! + return RowId.CompareTo(rowEdit.RowId) * -1; + } + + private string GetCommandText(string whereText) + { + string formatString = AssociatedObjectMetadata.IsMemoryOptimized + ? DeleteMemoryOptimizedStatement + : DeleteStatement; + + return string.Format(CultureInfo.InvariantCulture, formatString, + AssociatedObjectMetadata.EscapedMultipartName, whereText); + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs index e36c31e0..b69a4c9d 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Data.Common; using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; @@ -18,9 +19,10 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// /// Base class for row edit operations. Provides basic information and helper functionality /// that all RowEdit implementations can use. Defines functionality that must be implemented - /// in all child classes. + /// in all child classes. Implements a custom IComparable to enable sorting by type of the edit + /// and then by an overrideable /// - public abstract class RowEditBase + public abstract class RowEditBase : IComparable { /// /// Internal parameterless constructor, required for mocking @@ -58,8 +60,31 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// public IEditTableMetadata AssociatedObjectMetadata { get; } + /// + /// Sort ID for a row edit. Ensures that when a collection of RowEditBase objects are + /// sorted, the appropriate types are sorted to the top. + /// + protected abstract int SortId { get; } + #endregion + #region Abstract Methods + + /// + /// Applies the changes to the associated result set + /// + /// + /// Data reader from execution of the command to commit the change to the db + /// + public abstract Task ApplyChanges(DbDataReader dataReader); + + /// + /// Gets a command that will commit the change to the db + /// + /// The connection to associate the command to + /// Command to commit the change to the db + public abstract DbCommand GetCommand(DbConnection connection); + /// /// Converts the row edit into a SQL statement /// @@ -74,6 +99,10 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// The value of the cell after applying validation logic public abstract EditUpdateCellResult SetCell(int columnId, string newValue); + #endregion + + #region Protected Helper Methods + /// /// Performs validation of column ID and if column can be updated. /// @@ -146,7 +175,11 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement // we execute multiple row edits at once. string paramName = $"@Param{RowId}{col.Ordinal}"; cellDataClause = $"= {paramName}"; - output.Parameters.Add(new SqlParameter(paramName, col.DbColumn.SqlDbType)); + SqlParameter parameter = new SqlParameter(paramName, col.DbColumn.SqlDbType) + { + Value = cellData.RawObject + }; + output.Parameters.Add(parameter); } else { @@ -163,6 +196,66 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement return output; } + #endregion + + #region IComparable Implementation + + /// + /// Compares a row edit against another row edit. If they are the same type, then we + /// compare using an overrideable "same type" comparer. If they are different types, they + /// are sorted by their sort indexes. + /// + /// In general, RowCreate and RowUpdates are sorted to the top. RowDeletes are sorted last. + /// If there are ties, default behavior is to sort by row ID ascending. + /// + /// The other row edit to compare against + /// + /// A positive value if this edit should go first, a negative value if the other edit + /// should go first. 0 is returned if there is a tie. + /// + public int CompareTo(RowEditBase other) + { + // If the other is null, this one will come out on top + if (other == null) + { + return 1; + } + + // If types are the same, use the type's tiebreaking sorter + if (GetType() == other.GetType()) + { + return CompareToSameType(other); + } + + // If the type's sort index is the same, use our tiebreaking sorter + // If they are different, use that as the comparison + int sortIdComparison = SortId.CompareTo(other.SortId); + return sortIdComparison == 0 + ? CompareByRowId(other) + : sortIdComparison; + } + + /// + /// Default behavior for sorting if the two compared row edits are the same type. Sorts + /// by row ID ascending. + /// + /// The other row edit to compare against + protected virtual int CompareToSameType(RowEditBase rowEdit) + { + return CompareByRowId(rowEdit); + } + + /// + /// Compares two row edits by their row ID ascending. + /// + /// The other row edit to compare against + private int CompareByRowId(RowEditBase rowEdit) + { + return RowId.CompareTo(rowEdit.RowId); + } + + #endregion + /// /// Represents a WHERE clause that can be used for identifying a row in a table. /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs index 482e3fa5..95c5631d 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs @@ -5,12 +5,16 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Data; +using System.Data.Common; +using System.Data.SqlClient; using System.Linq; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.Utility; +using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement { @@ -19,8 +23,11 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement /// public sealed class RowUpdate : RowEditBase { - private const string UpdateStatement = "UPDATE {0} SET {1} {2}"; - private const string UpdateStatementMemoryOptimized = "UPDATE {0} WITH (SNAPSHOT) SET {1} {2}"; + private const string UpdateScriptStart = @"UPDATE {0}"; + private const string UpdateScriptStartMemOptimized = @"UPDATE {0} WITH (SNAPSHOT)"; + + private const string UpdateScript = @"{0} SET {1} {2}"; + private const string UpdateScriptOutput = @"{0} SET {1} OUTPUT {2} {3}"; private readonly Dictionary cellUpdates; private readonly IList associatedRow; @@ -38,6 +45,73 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement associatedRow = associatedResultSet.GetRow(rowId); } + /// + /// Sort order property. Sorts to same position as RowCreate + /// + protected override int SortId => 1; + + #region Public Methods + + /// + /// Applies the changes to the associated result set after successfully executing the + /// change on the database + /// + /// + /// Reader returned from the execution of the command to update a row. Should contain + /// a single row that represents all the values of the row. + /// + public override Task ApplyChanges(DbDataReader dataReader) + { + Validate.IsNotNull(nameof(dataReader), dataReader); + return AssociatedResultSet.UpdateRow(RowId, dataReader); + } + + /// + /// Generates a command that can be executed to update a row -- and return the contents of + /// the updated row. + /// + /// The connection the command should be associated with + /// Command to update the row + public override DbCommand GetCommand(DbConnection connection) + { + Validate.IsNotNull(nameof(connection), connection); + DbCommand command = connection.CreateCommand(); + + // Build the "SET" portion of the statement + List setComponents = new List(); + foreach (var updateElement in cellUpdates) + { + string formattedColumnName = SqlScriptFormatter.FormatIdentifier(updateElement.Value.Column.ColumnName); + string paramName = $"@Value{RowId}{updateElement.Key}"; + setComponents.Add($"{formattedColumnName} = {paramName}"); + SqlParameter parameter = new SqlParameter(paramName, updateElement.Value.Column.SqlDbType) + { + Value = updateElement.Value.Value + }; + command.Parameters.Add(parameter); + } + string setComponentsJoined = string.Join(", ", setComponents); + + // Build the "OUTPUT" portion of the statement + var outColumns = from c in AssociatedResultSet.Columns + let formatted = SqlScriptFormatter.FormatIdentifier(c.ColumnName) + select $"inserted.{formatted}"; + string outColumnsJoined = string.Join(", ", outColumns); + + // Get the where clause + WhereClause where = GetWhereClause(true); + command.Parameters.AddRange(where.Parameters.ToArray()); + + // Get the start of the statement + string statementStart = GetStatementStart(); + + // Put the whole #! together + command.CommandText = string.Format(UpdateScriptOutput, statementStart, setComponentsJoined, + outColumnsJoined, where.CommandText); + command.CommandType = CommandType.Text; + return command; + } + /// /// Constructs an update statement to change the associated row. /// @@ -45,7 +119,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement public override string GetScript() { // Build the "SET" portion of the statement - IEnumerable setComponents = cellUpdates.Values.Select(cellUpdate => + var setComponents = cellUpdates.Values.Select(cellUpdate => { string formattedColumnName = SqlScriptFormatter.FormatIdentifier(cellUpdate.Column.ColumnName); string formattedValue = SqlScriptFormatter.FormatValue(cellUpdate.Value, cellUpdate.Column); @@ -56,10 +130,11 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement // Get the where clause string whereClause = GetWhereClause(false).CommandText; - // Put it all together - string formatString = AssociatedObjectMetadata.IsMemoryOptimized ? UpdateStatementMemoryOptimized : UpdateStatement; - return string.Format(CultureInfo.InvariantCulture, formatString, - AssociatedObjectMetadata.EscapedMultipartName, setClause, whereClause); + // Get the start of the statement + string statementStart = GetStatementStart(); + + // Put the whole #! together + return string.Format(UpdateScript, statementStart, setClause, whereClause); } /// @@ -106,5 +181,16 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement IsRevert = false // If we're in this branch, it is not a revert }; } + + #endregion + + private string GetStatementStart() + { + string formatString = AssociatedObjectMetadata.IsMemoryOptimized + ? UpdateScriptStartMemOptimized + : UpdateScriptStart; + + return string.Format(formatString, AssociatedObjectMetadata.EscapedMultipartName); + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 337cd3c6..693aa48e 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -27,7 +27,7 @@ namespace Microsoft.SqlTools.ServiceLayer Keys.Culture = value; } } - + public static string ConnectionServiceConnectErrorNullParams { @@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ConnectionServiceConnectErrorNullParams); } - } + } public static string ConnectionServiceListDbErrorNullOwnerUri { @@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ConnectionServiceListDbErrorNullOwnerUri); } - } + } public static string ConnectionServiceConnectionCanceled { @@ -51,7 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ConnectionServiceConnectionCanceled); } - } + } public static string ConnectionParamsValidateNullOwnerUri { @@ -59,7 +59,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ConnectionParamsValidateNullOwnerUri); } - } + } public static string ConnectionParamsValidateNullConnection { @@ -67,7 +67,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ConnectionParamsValidateNullConnection); } - } + } public static string ConnectionParamsValidateNullServerName { @@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ConnectionParamsValidateNullServerName); } - } + } public static string ErrorUnexpectedCodeObjectType { @@ -83,7 +83,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ErrorUnexpectedCodeObjectType); } - } + } public static string QueryServiceCancelAlreadyCompleted { @@ -91,7 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceCancelAlreadyCompleted); } - } + } public static string QueryServiceCancelDisposeFailed { @@ -99,7 +99,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceCancelDisposeFailed); } - } + } public static string QueryServiceQueryCancelled { @@ -107,7 +107,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceQueryCancelled); } - } + } public static string QueryServiceSubsetBatchNotCompleted { @@ -115,7 +115,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceSubsetBatchNotCompleted); } - } + } public static string QueryServiceSubsetBatchOutOfRange { @@ -123,7 +123,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceSubsetBatchOutOfRange); } - } + } public static string QueryServiceSubsetResultSetOutOfRange { @@ -131,7 +131,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceSubsetResultSetOutOfRange); } - } + } public static string QueryServiceDataReaderByteCountInvalid { @@ -139,7 +139,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceDataReaderByteCountInvalid); } - } + } public static string QueryServiceDataReaderCharCountInvalid { @@ -147,7 +147,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceDataReaderCharCountInvalid); } - } + } public static string QueryServiceDataReaderXmlCountInvalid { @@ -155,7 +155,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceDataReaderXmlCountInvalid); } - } + } public static string QueryServiceFileWrapperWriteOnly { @@ -163,7 +163,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceFileWrapperWriteOnly); } - } + } public static string QueryServiceFileWrapperNotInitialized { @@ -171,7 +171,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceFileWrapperNotInitialized); } - } + } public static string QueryServiceFileWrapperReadOnly { @@ -179,7 +179,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceFileWrapperReadOnly); } - } + } public static string QueryServiceAffectedOneRow { @@ -187,7 +187,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceAffectedOneRow); } - } + } public static string QueryServiceCompletedSuccessfully { @@ -195,7 +195,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceCompletedSuccessfully); } - } + } public static string QueryServiceColumnNull { @@ -203,7 +203,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceColumnNull); } - } + } public static string QueryServiceRequestsNoQuery { @@ -211,7 +211,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceRequestsNoQuery); } - } + } public static string QueryServiceQueryInvalidOwnerUri { @@ -219,7 +219,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceQueryInvalidOwnerUri); } - } + } public static string QueryServiceQueryInProgress { @@ -227,7 +227,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceQueryInProgress); } - } + } public static string QueryServiceMessageSenderNotSql { @@ -235,15 +235,15 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceMessageSenderNotSql); } - } + } - public static string QueryServiceResultSetReaderNull + public static string QueryServiceResultSetAddNoRows { get { - return Keys.GetString(Keys.QueryServiceResultSetReaderNull); + return Keys.GetString(Keys.QueryServiceResultSetAddNoRows); } - } + } public static string QueryServiceSaveAsResultSetNotComplete { @@ -251,7 +251,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceSaveAsResultSetNotComplete); } - } + } public static string QueryServiceSaveAsMiscStartingError { @@ -259,7 +259,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceSaveAsMiscStartingError); } - } + } public static string QueryServiceSaveAsInProgress { @@ -267,7 +267,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceSaveAsInProgress); } - } + } public static string QueryServiceResultSetNotRead { @@ -275,7 +275,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceResultSetNotRead); } - } + } public static string QueryServiceResultSetStartRowOutOfRange { @@ -283,7 +283,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceResultSetStartRowOutOfRange); } - } + } public static string QueryServiceResultSetRowCountOutOfRange { @@ -291,7 +291,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceResultSetRowCountOutOfRange); } - } + } public static string QueryServiceResultSetNoColumnSchema { @@ -299,7 +299,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceResultSetNoColumnSchema); } - } + } public static string QueryServiceExecutionPlanNotFound { @@ -307,7 +307,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.QueryServiceExecutionPlanNotFound); } - } + } public static string PeekDefinitionNoResultsError { @@ -315,7 +315,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.PeekDefinitionNoResultsError); } - } + } public static string PeekDefinitionDatabaseError { @@ -323,7 +323,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.PeekDefinitionDatabaseError); } - } + } public static string PeekDefinitionNotConnectedError { @@ -331,7 +331,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.PeekDefinitionNotConnectedError); } - } + } public static string PeekDefinitionTimedoutError { @@ -339,7 +339,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.PeekDefinitionTimedoutError); } - } + } public static string PeekDefinitionTypeNotSupportedError { @@ -347,7 +347,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.PeekDefinitionTypeNotSupportedError); } - } + } public static string ErrorEmptyStringReplacement { @@ -355,7 +355,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.ErrorEmptyStringReplacement); } - } + } public static string WorkspaceServicePositionLineOutOfRange { @@ -363,7 +363,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.WorkspaceServicePositionLineOutOfRange); } - } + } public static string EditDataSessionNotFound { @@ -371,7 +371,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataSessionNotFound); } - } + } public static string EditDataQueryNotCompleted { @@ -379,7 +379,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataQueryNotCompleted); } - } + } public static string EditDataQueryImproperResultSets { @@ -387,7 +387,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataQueryImproperResultSets); } - } + } public static string EditDataFailedAddRow { @@ -395,7 +395,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataFailedAddRow); } - } + } public static string EditDataRowOutOfRange { @@ -403,7 +403,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataRowOutOfRange); } - } + } public static string EditDataUpdatePending { @@ -411,7 +411,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataUpdatePending); } - } + } public static string EditDataUpdateNotPending { @@ -419,7 +419,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataUpdateNotPending); } - } + } public static string EditDataObjectMetadataNotFound { @@ -427,7 +427,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataObjectMetadataNotFound); } - } + } public static string EditDataInvalidFormatBinary { @@ -435,7 +435,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataInvalidFormatBinary); } - } + } public static string EditDataInvalidFormatBoolean { @@ -443,7 +443,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataInvalidFormatBoolean); } - } + } public static string EditDataCreateScriptMissingValue { @@ -451,7 +451,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataCreateScriptMissingValue); } - } + } public static string EditDataDeleteSetCell { @@ -459,7 +459,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataDeleteSetCell); } - } + } public static string EditDataColumnIdOutOfRange { @@ -467,7 +467,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataColumnIdOutOfRange); } - } + } public static string EditDataColumnCannotBeEdited { @@ -475,7 +475,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataColumnCannotBeEdited); } - } + } public static string EditDataColumnNoKeyColumns { @@ -483,7 +483,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataColumnNoKeyColumns); } - } + } public static string EditDataScriptFilePathNull { @@ -491,7 +491,15 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EditDataScriptFilePathNull); } - } + } + + public static string EditDataCommitInProgress + { + get + { + return Keys.GetString(Keys.EditDataCommitInProgress); + } + } public static string EE_BatchSqlMessageNoProcedureInfo { @@ -499,7 +507,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchSqlMessageNoProcedureInfo); } - } + } public static string EE_BatchSqlMessageWithProcedureInfo { @@ -507,7 +515,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchSqlMessageWithProcedureInfo); } - } + } public static string EE_BatchSqlMessageNoLineInfo { @@ -515,7 +523,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchSqlMessageNoLineInfo); } - } + } public static string EE_BatchError_Exception { @@ -523,7 +531,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchError_Exception); } - } + } public static string EE_BatchExecutionInfo_RowsAffected { @@ -531,7 +539,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchExecutionInfo_RowsAffected); } - } + } public static string EE_ExecutionNotYetCompleteError { @@ -539,7 +547,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ExecutionNotYetCompleteError); } - } + } public static string EE_ScriptError_Error { @@ -547,7 +555,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ScriptError_Error); } - } + } public static string EE_ScriptError_ParsingSyntax { @@ -555,7 +563,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ScriptError_ParsingSyntax); } - } + } public static string EE_ScriptError_FatalError { @@ -563,7 +571,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ScriptError_FatalError); } - } + } public static string EE_ExecutionInfo_FinalizingLoop { @@ -571,7 +579,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ExecutionInfo_FinalizingLoop); } - } + } public static string EE_ExecutionInfo_QueryCancelledbyUser { @@ -579,7 +587,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ExecutionInfo_QueryCancelledbyUser); } - } + } public static string EE_BatchExecutionError_Halting { @@ -587,7 +595,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchExecutionError_Halting); } - } + } public static string EE_BatchExecutionError_Ignoring { @@ -595,7 +603,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_BatchExecutionError_Ignoring); } - } + } public static string EE_ExecutionInfo_InitilizingLoop { @@ -603,7 +611,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ExecutionInfo_InitilizingLoop); } - } + } public static string EE_ExecutionError_CommandNotSupported { @@ -611,7 +619,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ExecutionError_CommandNotSupported); } - } + } public static string EE_ExecutionError_VariableNotFound { @@ -619,7 +627,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ExecutionError_VariableNotFound); } - } + } public static string BatchParserWrapperExecutionEngineError { @@ -627,7 +635,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParserWrapperExecutionEngineError); } - } + } public static string BatchParserWrapperExecutionError { @@ -635,7 +643,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParserWrapperExecutionError); } - } + } public static string BatchParserWrapperExecutionEngineBatchMessage { @@ -643,7 +651,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParserWrapperExecutionEngineBatchMessage); } - } + } public static string BatchParserWrapperExecutionEngineBatchResultSetProcessing { @@ -651,7 +659,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParserWrapperExecutionEngineBatchResultSetProcessing); } - } + } public static string BatchParserWrapperExecutionEngineBatchResultSetFinished { @@ -659,7 +667,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParserWrapperExecutionEngineBatchResultSetFinished); } - } + } public static string BatchParserWrapperExecutionEngineBatchCancelling { @@ -667,7 +675,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParserWrapperExecutionEngineBatchCancelling); } - } + } public static string EE_ScriptError_Warning { @@ -675,7 +683,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.EE_ScriptError_Warning); } - } + } public static string TroubleshootingAssistanceMessage { @@ -683,7 +691,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.TroubleshootingAssistanceMessage); } - } + } public static string BatchParser_CircularReference { @@ -691,7 +699,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParser_CircularReference); } - } + } public static string BatchParser_CommentNotTerminated { @@ -699,7 +707,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParser_CommentNotTerminated); } - } + } public static string BatchParser_StringNotTerminated { @@ -707,7 +715,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParser_StringNotTerminated); } - } + } public static string BatchParser_IncorrectSyntax { @@ -715,7 +723,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParser_IncorrectSyntax); } - } + } public static string BatchParser_VariableNotDefined { @@ -723,7 +731,7 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.BatchParser_VariableNotDefined); } - } + } public static string TestLocalizationConstant { @@ -731,77 +739,85 @@ namespace Microsoft.SqlTools.ServiceLayer { return Keys.GetString(Keys.TestLocalizationConstant); } - } + } + + public static string SqlScriptFormatterDecimalMissingPrecision + { + get + { + return Keys.GetString(Keys.SqlScriptFormatterDecimalMissingPrecision); + } + } public static string ConnectionServiceListDbErrorNotConnected(string uri) { return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri); - } + } public static string ConnectionServiceDbErrorDefaultNotConnected(string uri) { return Keys.GetString(Keys.ConnectionServiceDbErrorDefaultNotConnected, uri); - } + } public static string ConnectionServiceConnStringInvalidAuthType(string authType) { return Keys.GetString(Keys.ConnectionServiceConnStringInvalidAuthType, authType); - } + } public static string ConnectionServiceConnStringInvalidIntent(string intent) { return Keys.GetString(Keys.ConnectionServiceConnStringInvalidIntent, intent); - } + } public static string ConnectionParamsValidateNullSqlAuth(string component) { return Keys.GetString(Keys.ConnectionParamsValidateNullSqlAuth, component); - } + } public static string QueryServiceAffectedRows(long rows) { return Keys.GetString(Keys.QueryServiceAffectedRows, rows); - } + } public static string QueryServiceErrorFormat(int msg, int lvl, int state, int line, string newLine, string message) { return Keys.GetString(Keys.QueryServiceErrorFormat, msg, lvl, state, line, newLine, message); - } + } public static string QueryServiceQueryFailed(string message) { return Keys.GetString(Keys.QueryServiceQueryFailed, message); - } + } public static string QueryServiceSaveAsFail(string fileName, string message) { return Keys.GetString(Keys.QueryServiceSaveAsFail, fileName, message); - } + } public static string PeekDefinitionAzureError(string errorMessage) { return Keys.GetString(Keys.PeekDefinitionAzureError, errorMessage); - } + } public static string PeekDefinitionError(string errorMessage) { return Keys.GetString(Keys.PeekDefinitionError, errorMessage); - } + } public static string WorkspaceServicePositionColumnOutOfRange(int line) { return Keys.GetString(Keys.WorkspaceServicePositionColumnOutOfRange, line); - } + } public static string WorkspaceServiceBufferPositionOutOfOrder(int sLine, int sCol, int eLine, int eCol) { return Keys.GetString(Keys.WorkspaceServiceBufferPositionOutOfOrder, sLine, sCol, eLine, eCol); - } + } public static string EditDataUnsupportedObjectType(string typeName) { return Keys.GetString(Keys.EditDataUnsupportedObjectType, typeName); - } + } [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Keys @@ -809,313 +825,319 @@ namespace Microsoft.SqlTools.ServiceLayer static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ServiceLayer.Localization.SR", typeof(SR).GetTypeInfo().Assembly); static CultureInfo _culture = null; - - - public const string ConnectionServiceConnectErrorNullParams = "ConnectionServiceConnectErrorNullParams"; - - - public const string ConnectionServiceListDbErrorNullOwnerUri = "ConnectionServiceListDbErrorNullOwnerUri"; - - - public const string ConnectionServiceListDbErrorNotConnected = "ConnectionServiceListDbErrorNotConnected"; - - - public const string ConnectionServiceDbErrorDefaultNotConnected = "ConnectionServiceDbErrorDefaultNotConnected"; - - - public const string ConnectionServiceConnStringInvalidAuthType = "ConnectionServiceConnStringInvalidAuthType"; - - - public const string ConnectionServiceConnStringInvalidIntent = "ConnectionServiceConnStringInvalidIntent"; - - - public const string ConnectionServiceConnectionCanceled = "ConnectionServiceConnectionCanceled"; - - - public const string ConnectionParamsValidateNullOwnerUri = "ConnectionParamsValidateNullOwnerUri"; - - - public const string ConnectionParamsValidateNullConnection = "ConnectionParamsValidateNullConnection"; - - - public const string ConnectionParamsValidateNullServerName = "ConnectionParamsValidateNullServerName"; - - - public const string ConnectionParamsValidateNullSqlAuth = "ConnectionParamsValidateNullSqlAuth"; - - - public const string ErrorUnexpectedCodeObjectType = "ErrorUnexpectedCodeObjectType"; - - - public const string QueryServiceCancelAlreadyCompleted = "QueryServiceCancelAlreadyCompleted"; - - - public const string QueryServiceCancelDisposeFailed = "QueryServiceCancelDisposeFailed"; - - - public const string QueryServiceQueryCancelled = "QueryServiceQueryCancelled"; - - - public const string QueryServiceSubsetBatchNotCompleted = "QueryServiceSubsetBatchNotCompleted"; - - - public const string QueryServiceSubsetBatchOutOfRange = "QueryServiceSubsetBatchOutOfRange"; - - - public const string QueryServiceSubsetResultSetOutOfRange = "QueryServiceSubsetResultSetOutOfRange"; - - - public const string QueryServiceDataReaderByteCountInvalid = "QueryServiceDataReaderByteCountInvalid"; - - - public const string QueryServiceDataReaderCharCountInvalid = "QueryServiceDataReaderCharCountInvalid"; - - - public const string QueryServiceDataReaderXmlCountInvalid = "QueryServiceDataReaderXmlCountInvalid"; - - - public const string QueryServiceFileWrapperWriteOnly = "QueryServiceFileWrapperWriteOnly"; - - - public const string QueryServiceFileWrapperNotInitialized = "QueryServiceFileWrapperNotInitialized"; - - - public const string QueryServiceFileWrapperReadOnly = "QueryServiceFileWrapperReadOnly"; - - - public const string QueryServiceAffectedOneRow = "QueryServiceAffectedOneRow"; - - - public const string QueryServiceAffectedRows = "QueryServiceAffectedRows"; - - - public const string QueryServiceCompletedSuccessfully = "QueryServiceCompletedSuccessfully"; - - - public const string QueryServiceErrorFormat = "QueryServiceErrorFormat"; - - - public const string QueryServiceQueryFailed = "QueryServiceQueryFailed"; - - - public const string QueryServiceColumnNull = "QueryServiceColumnNull"; - - - public const string QueryServiceRequestsNoQuery = "QueryServiceRequestsNoQuery"; - - - public const string QueryServiceQueryInvalidOwnerUri = "QueryServiceQueryInvalidOwnerUri"; - - - public const string QueryServiceQueryInProgress = "QueryServiceQueryInProgress"; - - - public const string QueryServiceMessageSenderNotSql = "QueryServiceMessageSenderNotSql"; - - - public const string QueryServiceResultSetReaderNull = "QueryServiceResultSetReaderNull"; - - - public const string QueryServiceSaveAsResultSetNotComplete = "QueryServiceSaveAsResultSetNotComplete"; - - - public const string QueryServiceSaveAsMiscStartingError = "QueryServiceSaveAsMiscStartingError"; - - - public const string QueryServiceSaveAsInProgress = "QueryServiceSaveAsInProgress"; - - - public const string QueryServiceSaveAsFail = "QueryServiceSaveAsFail"; - - - public const string QueryServiceResultSetNotRead = "QueryServiceResultSetNotRead"; - - - public const string QueryServiceResultSetStartRowOutOfRange = "QueryServiceResultSetStartRowOutOfRange"; - - - public const string QueryServiceResultSetRowCountOutOfRange = "QueryServiceResultSetRowCountOutOfRange"; - - - public const string QueryServiceResultSetNoColumnSchema = "QueryServiceResultSetNoColumnSchema"; - - - public const string QueryServiceExecutionPlanNotFound = "QueryServiceExecutionPlanNotFound"; - - - public const string PeekDefinitionAzureError = "PeekDefinitionAzureError"; - - - public const string PeekDefinitionError = "PeekDefinitionError"; - - - public const string PeekDefinitionNoResultsError = "PeekDefinitionNoResultsError"; - - - public const string PeekDefinitionDatabaseError = "PeekDefinitionDatabaseError"; - - - public const string PeekDefinitionNotConnectedError = "PeekDefinitionNotConnectedError"; - - - public const string PeekDefinitionTimedoutError = "PeekDefinitionTimedoutError"; - - - public const string PeekDefinitionTypeNotSupportedError = "PeekDefinitionTypeNotSupportedError"; - - - public const string ErrorEmptyStringReplacement = "ErrorEmptyStringReplacement"; - - - public const string WorkspaceServicePositionLineOutOfRange = "WorkspaceServicePositionLineOutOfRange"; - - - public const string WorkspaceServicePositionColumnOutOfRange = "WorkspaceServicePositionColumnOutOfRange"; - - - public const string WorkspaceServiceBufferPositionOutOfOrder = "WorkspaceServiceBufferPositionOutOfOrder"; - - - public const string EditDataSessionNotFound = "EditDataSessionNotFound"; - - - public const string EditDataUnsupportedObjectType = "EditDataUnsupportedObjectType"; - - - public const string EditDataQueryNotCompleted = "EditDataQueryNotCompleted"; - - - public const string EditDataQueryImproperResultSets = "EditDataQueryImproperResultSets"; - - - public const string EditDataFailedAddRow = "EditDataFailedAddRow"; - - - public const string EditDataRowOutOfRange = "EditDataRowOutOfRange"; - - - public const string EditDataUpdatePending = "EditDataUpdatePending"; - - - public const string EditDataUpdateNotPending = "EditDataUpdateNotPending"; - - - public const string EditDataObjectMetadataNotFound = "EditDataObjectMetadataNotFound"; - - - public const string EditDataInvalidFormatBinary = "EditDataInvalidFormatBinary"; - - - public const string EditDataInvalidFormatBoolean = "EditDataInvalidFormatBoolean"; - - - public const string EditDataCreateScriptMissingValue = "EditDataCreateScriptMissingValue"; - - - public const string EditDataDeleteSetCell = "EditDataDeleteSetCell"; - - - public const string EditDataColumnIdOutOfRange = "EditDataColumnIdOutOfRange"; - - - public const string EditDataColumnCannotBeEdited = "EditDataColumnCannotBeEdited"; - - - public const string EditDataColumnNoKeyColumns = "EditDataColumnNoKeyColumns"; - - - public const string EditDataScriptFilePathNull = "EditDataScriptFilePathNull"; - - - public const string EE_BatchSqlMessageNoProcedureInfo = "EE_BatchSqlMessageNoProcedureInfo"; - - - public const string EE_BatchSqlMessageWithProcedureInfo = "EE_BatchSqlMessageWithProcedureInfo"; - - - public const string EE_BatchSqlMessageNoLineInfo = "EE_BatchSqlMessageNoLineInfo"; - - - public const string EE_BatchError_Exception = "EE_BatchError_Exception"; - - - public const string EE_BatchExecutionInfo_RowsAffected = "EE_BatchExecutionInfo_RowsAffected"; - - - public const string EE_ExecutionNotYetCompleteError = "EE_ExecutionNotYetCompleteError"; - - - public const string EE_ScriptError_Error = "EE_ScriptError_Error"; - - - public const string EE_ScriptError_ParsingSyntax = "EE_ScriptError_ParsingSyntax"; - - - public const string EE_ScriptError_FatalError = "EE_ScriptError_FatalError"; - - - public const string EE_ExecutionInfo_FinalizingLoop = "EE_ExecutionInfo_FinalizingLoop"; - - - public const string EE_ExecutionInfo_QueryCancelledbyUser = "EE_ExecutionInfo_QueryCancelledbyUser"; - - - public const string EE_BatchExecutionError_Halting = "EE_BatchExecutionError_Halting"; - - - public const string EE_BatchExecutionError_Ignoring = "EE_BatchExecutionError_Ignoring"; - - - public const string EE_ExecutionInfo_InitilizingLoop = "EE_ExecutionInfo_InitilizingLoop"; - - - public const string EE_ExecutionError_CommandNotSupported = "EE_ExecutionError_CommandNotSupported"; - - - public const string EE_ExecutionError_VariableNotFound = "EE_ExecutionError_VariableNotFound"; - - - public const string BatchParserWrapperExecutionEngineError = "BatchParserWrapperExecutionEngineError"; - - - public const string BatchParserWrapperExecutionError = "BatchParserWrapperExecutionError"; - - - public const string BatchParserWrapperExecutionEngineBatchMessage = "BatchParserWrapperExecutionEngineBatchMessage"; - - - public const string BatchParserWrapperExecutionEngineBatchResultSetProcessing = "BatchParserWrapperExecutionEngineBatchResultSetProcessing"; - - - public const string BatchParserWrapperExecutionEngineBatchResultSetFinished = "BatchParserWrapperExecutionEngineBatchResultSetFinished"; - - - public const string BatchParserWrapperExecutionEngineBatchCancelling = "BatchParserWrapperExecutionEngineBatchCancelling"; - - - public const string EE_ScriptError_Warning = "EE_ScriptError_Warning"; - - - public const string TroubleshootingAssistanceMessage = "TroubleshootingAssistanceMessage"; - - - public const string BatchParser_CircularReference = "BatchParser_CircularReference"; - - - public const string BatchParser_CommentNotTerminated = "BatchParser_CommentNotTerminated"; - - - public const string BatchParser_StringNotTerminated = "BatchParser_StringNotTerminated"; - - - public const string BatchParser_IncorrectSyntax = "BatchParser_IncorrectSyntax"; - - - public const string BatchParser_VariableNotDefined = "BatchParser_VariableNotDefined"; - - - public const string TestLocalizationConstant = "TestLocalizationConstant"; - + + + public const string ConnectionServiceConnectErrorNullParams = "ConnectionServiceConnectErrorNullParams"; + + + public const string ConnectionServiceListDbErrorNullOwnerUri = "ConnectionServiceListDbErrorNullOwnerUri"; + + + public const string ConnectionServiceListDbErrorNotConnected = "ConnectionServiceListDbErrorNotConnected"; + + + public const string ConnectionServiceDbErrorDefaultNotConnected = "ConnectionServiceDbErrorDefaultNotConnected"; + + + public const string ConnectionServiceConnStringInvalidAuthType = "ConnectionServiceConnStringInvalidAuthType"; + + + public const string ConnectionServiceConnStringInvalidIntent = "ConnectionServiceConnStringInvalidIntent"; + + + public const string ConnectionServiceConnectionCanceled = "ConnectionServiceConnectionCanceled"; + + + public const string ConnectionParamsValidateNullOwnerUri = "ConnectionParamsValidateNullOwnerUri"; + + + public const string ConnectionParamsValidateNullConnection = "ConnectionParamsValidateNullConnection"; + + + public const string ConnectionParamsValidateNullServerName = "ConnectionParamsValidateNullServerName"; + + + public const string ConnectionParamsValidateNullSqlAuth = "ConnectionParamsValidateNullSqlAuth"; + + + public const string ErrorUnexpectedCodeObjectType = "ErrorUnexpectedCodeObjectType"; + + + public const string QueryServiceCancelAlreadyCompleted = "QueryServiceCancelAlreadyCompleted"; + + + public const string QueryServiceCancelDisposeFailed = "QueryServiceCancelDisposeFailed"; + + + public const string QueryServiceQueryCancelled = "QueryServiceQueryCancelled"; + + + public const string QueryServiceSubsetBatchNotCompleted = "QueryServiceSubsetBatchNotCompleted"; + + + public const string QueryServiceSubsetBatchOutOfRange = "QueryServiceSubsetBatchOutOfRange"; + + + public const string QueryServiceSubsetResultSetOutOfRange = "QueryServiceSubsetResultSetOutOfRange"; + + + public const string QueryServiceDataReaderByteCountInvalid = "QueryServiceDataReaderByteCountInvalid"; + + + public const string QueryServiceDataReaderCharCountInvalid = "QueryServiceDataReaderCharCountInvalid"; + + + public const string QueryServiceDataReaderXmlCountInvalid = "QueryServiceDataReaderXmlCountInvalid"; + + + public const string QueryServiceFileWrapperWriteOnly = "QueryServiceFileWrapperWriteOnly"; + + + public const string QueryServiceFileWrapperNotInitialized = "QueryServiceFileWrapperNotInitialized"; + + + public const string QueryServiceFileWrapperReadOnly = "QueryServiceFileWrapperReadOnly"; + + + public const string QueryServiceAffectedOneRow = "QueryServiceAffectedOneRow"; + + + public const string QueryServiceAffectedRows = "QueryServiceAffectedRows"; + + + public const string QueryServiceCompletedSuccessfully = "QueryServiceCompletedSuccessfully"; + + + public const string QueryServiceErrorFormat = "QueryServiceErrorFormat"; + + + public const string QueryServiceQueryFailed = "QueryServiceQueryFailed"; + + + public const string QueryServiceColumnNull = "QueryServiceColumnNull"; + + + public const string QueryServiceRequestsNoQuery = "QueryServiceRequestsNoQuery"; + + + public const string QueryServiceQueryInvalidOwnerUri = "QueryServiceQueryInvalidOwnerUri"; + + + public const string QueryServiceQueryInProgress = "QueryServiceQueryInProgress"; + + + public const string QueryServiceMessageSenderNotSql = "QueryServiceMessageSenderNotSql"; + + + public const string QueryServiceResultSetAddNoRows = "QueryServiceResultSetAddNoRows"; + + + public const string QueryServiceSaveAsResultSetNotComplete = "QueryServiceSaveAsResultSetNotComplete"; + + + public const string QueryServiceSaveAsMiscStartingError = "QueryServiceSaveAsMiscStartingError"; + + + public const string QueryServiceSaveAsInProgress = "QueryServiceSaveAsInProgress"; + + + public const string QueryServiceSaveAsFail = "QueryServiceSaveAsFail"; + + + public const string QueryServiceResultSetNotRead = "QueryServiceResultSetNotRead"; + + + public const string QueryServiceResultSetStartRowOutOfRange = "QueryServiceResultSetStartRowOutOfRange"; + + + public const string QueryServiceResultSetRowCountOutOfRange = "QueryServiceResultSetRowCountOutOfRange"; + + + public const string QueryServiceResultSetNoColumnSchema = "QueryServiceResultSetNoColumnSchema"; + + + public const string QueryServiceExecutionPlanNotFound = "QueryServiceExecutionPlanNotFound"; + + + public const string PeekDefinitionAzureError = "PeekDefinitionAzureError"; + + + public const string PeekDefinitionError = "PeekDefinitionError"; + + + public const string PeekDefinitionNoResultsError = "PeekDefinitionNoResultsError"; + + + public const string PeekDefinitionDatabaseError = "PeekDefinitionDatabaseError"; + + + public const string PeekDefinitionNotConnectedError = "PeekDefinitionNotConnectedError"; + + + public const string PeekDefinitionTimedoutError = "PeekDefinitionTimedoutError"; + + + public const string PeekDefinitionTypeNotSupportedError = "PeekDefinitionTypeNotSupportedError"; + + + public const string ErrorEmptyStringReplacement = "ErrorEmptyStringReplacement"; + + + public const string WorkspaceServicePositionLineOutOfRange = "WorkspaceServicePositionLineOutOfRange"; + + + public const string WorkspaceServicePositionColumnOutOfRange = "WorkspaceServicePositionColumnOutOfRange"; + + + public const string WorkspaceServiceBufferPositionOutOfOrder = "WorkspaceServiceBufferPositionOutOfOrder"; + + + public const string EditDataSessionNotFound = "EditDataSessionNotFound"; + + + public const string EditDataUnsupportedObjectType = "EditDataUnsupportedObjectType"; + + + public const string EditDataQueryNotCompleted = "EditDataQueryNotCompleted"; + + + public const string EditDataQueryImproperResultSets = "EditDataQueryImproperResultSets"; + + + public const string EditDataFailedAddRow = "EditDataFailedAddRow"; + + + public const string EditDataRowOutOfRange = "EditDataRowOutOfRange"; + + + public const string EditDataUpdatePending = "EditDataUpdatePending"; + + + public const string EditDataUpdateNotPending = "EditDataUpdateNotPending"; + + + public const string EditDataObjectMetadataNotFound = "EditDataObjectMetadataNotFound"; + + + public const string EditDataInvalidFormatBinary = "EditDataInvalidFormatBinary"; + + + public const string EditDataInvalidFormatBoolean = "EditDataInvalidFormatBoolean"; + + + public const string EditDataCreateScriptMissingValue = "EditDataCreateScriptMissingValue"; + + + public const string EditDataDeleteSetCell = "EditDataDeleteSetCell"; + + + public const string EditDataColumnIdOutOfRange = "EditDataColumnIdOutOfRange"; + + + public const string EditDataColumnCannotBeEdited = "EditDataColumnCannotBeEdited"; + + + public const string EditDataColumnNoKeyColumns = "EditDataColumnNoKeyColumns"; + + + public const string EditDataScriptFilePathNull = "EditDataScriptFilePathNull"; + + + public const string EditDataCommitInProgress = "EditDataCommitInProgress"; + + + public const string EE_BatchSqlMessageNoProcedureInfo = "EE_BatchSqlMessageNoProcedureInfo"; + + + public const string EE_BatchSqlMessageWithProcedureInfo = "EE_BatchSqlMessageWithProcedureInfo"; + + + public const string EE_BatchSqlMessageNoLineInfo = "EE_BatchSqlMessageNoLineInfo"; + + + public const string EE_BatchError_Exception = "EE_BatchError_Exception"; + + + public const string EE_BatchExecutionInfo_RowsAffected = "EE_BatchExecutionInfo_RowsAffected"; + + + public const string EE_ExecutionNotYetCompleteError = "EE_ExecutionNotYetCompleteError"; + + + public const string EE_ScriptError_Error = "EE_ScriptError_Error"; + + + public const string EE_ScriptError_ParsingSyntax = "EE_ScriptError_ParsingSyntax"; + + + public const string EE_ScriptError_FatalError = "EE_ScriptError_FatalError"; + + + public const string EE_ExecutionInfo_FinalizingLoop = "EE_ExecutionInfo_FinalizingLoop"; + + + public const string EE_ExecutionInfo_QueryCancelledbyUser = "EE_ExecutionInfo_QueryCancelledbyUser"; + + + public const string EE_BatchExecutionError_Halting = "EE_BatchExecutionError_Halting"; + + + public const string EE_BatchExecutionError_Ignoring = "EE_BatchExecutionError_Ignoring"; + + + public const string EE_ExecutionInfo_InitilizingLoop = "EE_ExecutionInfo_InitilizingLoop"; + + + public const string EE_ExecutionError_CommandNotSupported = "EE_ExecutionError_CommandNotSupported"; + + + public const string EE_ExecutionError_VariableNotFound = "EE_ExecutionError_VariableNotFound"; + + + public const string BatchParserWrapperExecutionEngineError = "BatchParserWrapperExecutionEngineError"; + + + public const string BatchParserWrapperExecutionError = "BatchParserWrapperExecutionError"; + + + public const string BatchParserWrapperExecutionEngineBatchMessage = "BatchParserWrapperExecutionEngineBatchMessage"; + + + public const string BatchParserWrapperExecutionEngineBatchResultSetProcessing = "BatchParserWrapperExecutionEngineBatchResultSetProcessing"; + + + public const string BatchParserWrapperExecutionEngineBatchResultSetFinished = "BatchParserWrapperExecutionEngineBatchResultSetFinished"; + + + public const string BatchParserWrapperExecutionEngineBatchCancelling = "BatchParserWrapperExecutionEngineBatchCancelling"; + + + public const string EE_ScriptError_Warning = "EE_ScriptError_Warning"; + + + public const string TroubleshootingAssistanceMessage = "TroubleshootingAssistanceMessage"; + + + public const string BatchParser_CircularReference = "BatchParser_CircularReference"; + + + public const string BatchParser_CommentNotTerminated = "BatchParser_CommentNotTerminated"; + + + public const string BatchParser_StringNotTerminated = "BatchParser_StringNotTerminated"; + + + public const string BatchParser_IncorrectSyntax = "BatchParser_IncorrectSyntax"; + + + public const string BatchParser_VariableNotDefined = "BatchParser_VariableNotDefined"; + + + public const string TestLocalizationConstant = "TestLocalizationConstant"; + + + public const string SqlScriptFormatterDecimalMissingPrecision = "SqlScriptFormatterDecimalMissingPrecision"; + private Keys() { } @@ -1136,31 +1158,31 @@ namespace Microsoft.SqlTools.ServiceLayer { return resourceManager.GetString(key, _culture); } - + public static string GetString(string key, object arg0) { return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0); } - + public static string GetString(string key, object arg0, object arg1) { return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0, arg1); } - + public static string GetString(string key, object arg0, object arg1, object arg2, object arg3) { return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0, arg1, arg2, arg3); } - + public static string GetString(string key, object arg0, object arg1, object arg2, object arg3, object arg4, object arg5) { return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0, arg1, arg2, arg3, arg4, arg5); } - - } - } -} + + } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.es.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.es.resx index f43e5c5e..d65a6f96 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.es.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.es.resx @@ -1,18 +1,96 @@  + + + + + + + + + + + + + + + + + + + - + + @@ -27,5 +105,19 @@ -text/microsoft-resx1.3System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089ES_LOCALIZATION - \ No newline at end of file + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ES_LOCALIZATION + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index 30ff168c..fbb62c0f 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -120,423 +120,431 @@ Connection parameters cannot be null - + OwnerUri cannot be null or empty - + SpecifiedUri '{0}' does not have existing connection . Parameters: 0 - uri (string) - + Specified URI '{0}' does not have a default connection . Parameters: 0 - uri (string) - + Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'. . Parameters: 0 - authType (string) - + Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'. . Parameters: 0 - intent (string) - + Connection canceled - + OwnerUri cannot be null or empty - + Connection details object cannot be null - + ServerName cannot be null or empty - + {0} cannot be null or empty when using SqlLogin authentication . Parameters: 0 - component (string) - + Cannot convert SqlCodeObject Type {0} to Type {1} - + The query has already completed, it cannot be cancelled - + Query successfully cancelled, failed to dispose query. Owner URI not found. - + Query was canceled by user - + The batch has not completed, yet - + Batch index cannot be less than 0 or greater than the number of batches - + Result set index cannot be less than 0 or greater than the number of result sets - + Maximum number of bytes to return must be greater than zero - + Maximum number of chars to return must be greater than zero - + Maximum number of XML bytes to return must be greater than zero - + Access method cannot be write-only - + FileStreamWrapper must be initialized before performing operations - + This FileStreamWrapper cannot be used for writing - + (1 row affected) - + ({0} rows affected) . Parameters: 0 - rows (long) - + Commands completed successfully. - + Msg {0}, Level {1}, State {2}, Line {3}{4}{5} . Parameters: 0 - msg (int), 1 - lvl (int), 2 - state (int), 3 - line (int), 4 - newLine (string), 5 - message (string) - + Query failed: {0} . Parameters: 0 - message (string) - + (No column name) - + The requested query does not exist - + This editor is not connected to a database - + A query is already in progress for this editor session. Please cancel this query or wait for its completion. - + Sender for OnInfoMessage event must be a SqlConnection - - - Reader cannot be null + + + Cannot add row to result buffer, data reader does not contain rows - + Result cannot be saved until query execution has completed - + Internal error occurred while starting save task - + A save request to the same path is in progress - + Failed to save {0}: {1} . Parameters: 0 - fileName (string), 1 - message (string) - + Cannot read subset unless the results have been read from the server - + Start row cannot be less than 0 or greater than the number of rows in the result set - + Row count must be a positive integer - + Could not retrieve column schema for result set - + Could not retrieve an execution plan from the result set - + This feature is currently not supported on Azure SQL DB and Data Warehouse: {0} . Parameters: 0 - errorMessage (string) - + An unexpected error occurred during Peek Definition execution: {0} . Parameters: 0 - errorMessage (string) - + No results were found. - + No database object was retrieved. - + Please connect to a server. - + Operation timed out. - + This object type is currently not supported by this feature. - + Replacement of an empty string by an empty string. - + Position is outside of file line range - + Position is outside of column range for line {0} . Parameters: 0 - line (int) - + Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3}) . Parameters: 0 - sLine (int), 1 - sCol (int), 2 - eLine (int), 3 - eCol (int) - + Edit session does not exist. - + Database object {0} cannot be used for editing. . Parameters: 0 - typeName (string) - + Query has not completed execution - + Query did not generate exactly one result set - + Failed to add new row to update cache - + Given row ID is outside the range of rows in the edit cache - + An update is already pending for this row and must be reverted first - + Given row ID does not have pending updated - + Table or view metadata could not be found - + Invalid format for binary column - + Allowed values for boolean columns are 0, 1, "true", or "false" - + A required cell value is missing - + A delete is pending for this row, a cell update cannot be applied. - + Column ID must be in the range of columns for the query - + Column cannot be edited - + No key columns were found - + An output filename must be provided - + + + A commit task is in progress. Please wait for completion. + + Msg {0}, Level {1}, State {2}, Line {3} - + Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4} - + Msg {0}, Level {1}, State {2} - + An error occurred while the batch was being processed. The error message is: {0} - + ({0} row(s) affected) - + The previous execution is not yet complete. - + A scripting error occurred. - + Incorrect syntax was encountered while {0} was being parsed. - + A fatal error occurred. - + Execution completed {0} times... - + You cancelled the query. - + An error occurred while the batch was being executed. - + An error occurred while the batch was being executed, but the error has been ignored. - + Starting execution loop of {0} times... - + Command {0} is not supported. - + The variable {0} could not be found. - + SQL Execution error: {0} - + Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3} - + Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1} - + Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1} - + Batch parser wrapper execution engine batch ResultSet finished. - + Canceling batch parser wrapper batch execution. - + Scripting warning. - + For more information about this error, see the troubleshooting topics in the product documentation. - + File '{0}' recursively included. - + Missing end comment mark '*/'. - + Unclosed quotation mark after the character string. - + Incorrect syntax was encountered while parsing '{0}'. - + Variable {0} is not defined. - + EN_LOCALIZATION - - + + + Decimal column is missing numeric precision or numeric scale + + + diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings index 52b3d24f..04784fe4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings @@ -37,7 +37,6 @@ ConnectionServiceConnStringInvalidIntent(string intent) = Invalid value '{0}' fo ConnectionServiceConnectionCanceled = Connection canceled -###### ### Connection Params Validation Errors ConnectionParamsValidateNullOwnerUri = OwnerUri cannot be null or empty @@ -110,7 +109,7 @@ QueryServiceQueryInProgress = A query is already in progress for this editor ses QueryServiceMessageSenderNotSql = Sender for OnInfoMessage event must be a SqlConnection -QueryServiceResultSetReaderNull = Reader cannot be null +QueryServiceResultSetAddNoRows = Cannot add row to result buffer, data reader does not contain rows ### Save As Requests @@ -199,6 +198,8 @@ EditDataColumnNoKeyColumns = No key columns were found EditDataScriptFilePathNull = An output filename must be provided +EditDataCommitInProgress = A commit task is in progress. Please wait for completion. + ############################################################################ # DacFx Resources @@ -264,3 +265,8 @@ BatchParser_VariableNotDefined = Variable {0} is not defined. # Workspace Service TestLocalizationConstant = EN_LOCALIZATION + +############################################################################ +# Utilities + +SqlScriptFormatterDecimalMissingPrecision = Decimal column is missing numeric precision or numeric scale diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf index 3af5e24e..810e675b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf @@ -169,11 +169,6 @@ Sender for OnInfoMessage event must be a SqlConnection - - Reader cannot be null - Reader cannot be null - - Result cannot be saved until query execution has completed Result cannot be saved until query execution has completed @@ -526,6 +521,21 @@ . Parameters: 0 - uri (string) + + A commit task is in progress. Please wait for completion. + A commit task is in progress. Please wait for completion. + + + + Decimal column is missing numeric precision or numeric scale + Decimal column is missing numeric precision or numeric scale + + + + Cannot add row to result buffer, data reader does not contain rows + Cannot add row to result buffer, data reader does not contain rows + + \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs index 1e7518d6..a912d30f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs @@ -286,7 +286,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution } // This resultset has results (ie, SELECT/etc queries) - ResultSet resultSet = new ResultSet(reader, resultSetOrdinal, Id, outputFileFactory); + ResultSet resultSet = new ResultSet(resultSetOrdinal, Id, outputFileFactory); resultSet.ResultCompletion += ResultSetCompletion; // Add the result set to the results of the query @@ -297,7 +297,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution } // Read until we hit the end of the result set - await resultSet.ReadResultToEnd(cancellationToken).ConfigureAwait(false); + await resultSet.ReadResultToEnd(reader, cancellationToken).ConfigureAwait(false); } while (await reader.NextResultAsync(cancellationToken)); diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbColumnWrapper.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbColumnWrapper.cs index 7f77e45e..dd60564e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbColumnWrapper.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbColumnWrapper.cs @@ -85,7 +85,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts IsIdentity = column.IsIdentity; IsKey = column.IsKey; IsLong = column.IsLong; - IsReadOnly = column.IsLong; + IsReadOnly = column.IsReadOnly; IsUnique = column.IsUnique; NumericPrecision = column.NumericPrecision; NumericScale = column.NumericScale; diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs index bc3fb3c7..d46d9e6f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs @@ -17,7 +17,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage { int WriteRow(StorageDataReader dataReader); void WriteRow(IList row, IList columns); - + void Seek(long offset); void FlushBuffer(); } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs index c3359726..1c70e51b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs @@ -75,6 +75,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage /// The list of columns to output public abstract void WriteRow(IList row, IList columns); + /// + /// Not implemented, do not use. + /// + [Obsolete] + public void Seek(long offset) + { + throw new InvalidOperationException("SaveAs writers are meant to be written once contiguously."); + } + /// /// Flushes the file stream buffer /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs index 42f1c512..df93c72f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs @@ -212,6 +212,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage throw new InvalidOperationException("This type of writer is meant to write values from a DbDataReader only."); } + /// + /// Seeks to a given offset in the file, relative to the beginning of the file + /// + public void Seek(long offset) + { + fileStream.Seek(offset, SeekOrigin.Begin); + } + /// /// Flushes the internal buffer to the file stream /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs index 8f59425b..55747e4e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs @@ -33,11 +33,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution #region Member Variables - /// - /// The reader to use for this resultset - /// - private readonly StorageDataReader dataReader; - /// /// For IDisposable pattern, whether or not object has been disposed /// @@ -69,30 +64,37 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution /// private readonly string outputFileName; + /// + /// Row count to use in special scenarios where we want to override the number of rows. + /// + private long? rowCountOverride; + /// /// The special action which applied to this result set /// private readonly SpecialAction specialAction; + /// + /// Total number of bytes written to the file. Used to jump to end of the file for append + /// scenarios. Internal for unit test validation. + /// + internal long totalBytesWritten; + #endregion /// /// Creates a new result set and initializes its state /// - /// The reader from executing a query /// The ID of the resultset, the ordinal of the result within the batch /// The ID of the batch, the ordinal of the batch within the query /// Factory for creating a reader/writer - public ResultSet(DbDataReader reader, int ordinal, int batchOrdinal, IFileStreamFactory factory) + public ResultSet(int ordinal, int batchOrdinal, IFileStreamFactory factory) { - // Sanity check to make sure we got a reader - Validate.IsNotNull(nameof(reader), SR.QueryServiceResultSetReaderNull); - - dataReader = new StorageDataReader(reader); Id = ordinal; BatchId = batchOrdinal; // Initialize the storage + totalBytesWritten = 0; outputFileName = factory.CreateFile(); fileOffsets = new LongList(); specialAction = new SpecialAction(); @@ -103,7 +105,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution SaveTasks = new ConcurrentDictionary(); } - #region Properties + #region Eventing /// /// Asynchronous handler for when saving query results succeeds @@ -129,6 +131,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution /// public event ResultSetAsyncEventHandler ResultCompletion; + #endregion + + #region Properties + /// /// Whether the resultSet is in the process of being disposed /// @@ -153,7 +159,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution /// /// The number of rows for this result set /// - public long RowCount { get; private set; } + public long RowCount => rowCountOverride ?? fileOffsets.Count; /// /// All save tasks currently saving this ResultSet @@ -173,8 +179,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution Id = Id, BatchId = BatchId, RowCount = RowCount, - SpecialAction = ProcessSpecialAction() - + SpecialAction = hasBeenRead ? ProcessSpecialAction() : null }; } } @@ -183,6 +188,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution #region Public Methods + /// + /// Returns a specific row from the result set. + /// + /// + /// Creates a new file reader for a single reader. This method should only be used for one + /// off requests, not for requesting a large subset of the results. + /// + /// The internal ID of the row to read + /// The requested row public IList GetRow(long rowId) { // Sanity check to make sure that results have been read beforehand @@ -313,14 +327,20 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution /// /// Reads from the reader until there are no more results to read /// + /// The data reader for getting results from the db /// Cancellation token for cancelling the query - public async Task ReadResultToEnd(CancellationToken cancellationToken) + public async Task ReadResultToEnd(DbDataReader dbDataReader, CancellationToken cancellationToken) { + // Sanity check to make sure we got a reader + Validate.IsNotNull(nameof(dbDataReader), dbDataReader); + try { // Mark that result has been read hasBeenRead = true; + StorageDataReader dataReader = new StorageDataReader(dbDataReader); + // Open a writer for the file var fileWriter = fileStreamFactory.GetWriter(outputFileName); using (fileWriter) @@ -331,13 +351,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution throw new InvalidOperationException(SR.QueryServiceResultSetNoColumnSchema); } Columns = dataReader.Columns; - long currentFileOffset = 0; - while (await dataReader.ReadAsync(cancellationToken)) { - RowCount++; - fileOffsets.Add(currentFileOffset); - currentFileOffset += fileWriter.WriteRow(dataReader); + fileOffsets.Add(totalBytesWritten); + totalBytesWritten += fileWriter.WriteRow(dataReader); } } // Check if resultset is 'for xml/json'. If it is, set isJson/isXml value in column metadata @@ -353,6 +370,50 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution } } + /// + /// Removes a row from the result set cache + /// + /// Internal ID of the row + public void RemoveRow(long internalId) + { + // Make sure that the results have been read + if (!hasBeenRead) + { + throw new InvalidOperationException(SR.QueryServiceResultSetNotRead); + } + + // Simply remove the row from the list of row offsets + fileOffsets.RemoveAt(internalId); + } + + /// + /// Adds a new row to the result set by reading the row from the provided db data reader + /// + /// The result of a command to insert a new row should be UNREAD + public async Task AddRow(DbDataReader dbDataReader) + { + // Write the new row to the end of the file + long newOffset = await AppendRowToBuffer(dbDataReader); + + // Add the row to file offset list + fileOffsets.Add(newOffset); + } + + /// + /// Updates the values in a row with the + /// + /// + /// + /// + public async Task UpdateRow(long rowId, DbDataReader dbDataReader) + { + // Write the updated row to the end of the file + long newOffset = await AppendRowToBuffer(dbDataReader); + + // Update the file offset of the row in question + fileOffsets[rowId] = newOffset; + } + /// /// Saves the contents of this result set to a file using the IFileStreamFactory provided /// @@ -508,13 +569,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution { Columns[0].IsXml = true; isSingleColumnXmlJsonResultSet = true; - RowCount = 1; + rowCountOverride = 1; } else if (Columns[0].ColumnName.Equals(NameOfForJsonColumn, StringComparison.Ordinal)) { Columns[0].IsJson = true; isSingleColumnXmlJsonResultSet = true; - RowCount = 1; + rowCountOverride = 1; } } } @@ -526,7 +587,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution { // Check if this result set is a showplan - if (dataReader.Columns.Length == 1 && string.Compare(dataReader.Columns[0].ColumnName, YukonXmlShowPlanColumn, StringComparison.OrdinalIgnoreCase) == 0) + if (Columns.Length == 1 && string.Compare(Columns[0].ColumnName, YukonXmlShowPlanColumn, StringComparison.OrdinalIgnoreCase) == 0) { specialAction.ExpectYukonXMLShowPlan = true; } @@ -534,6 +595,36 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution return specialAction; } + /// + /// Adds a single row to the end of the buffer file. INTENDED FOR SINGLE ROW INSERTION ONLY. + /// + /// An UNREAD db data reader + /// The offset into the file where the row was inserted + private async Task AppendRowToBuffer(DbDataReader dbDataReader) + { + Validate.IsNotNull(nameof(dbDataReader), dbDataReader); + if (!hasBeenRead) + { + throw new InvalidOperationException(SR.QueryServiceResultSetNotRead); + } + if (!dbDataReader.HasRows) + { + throw new InvalidOperationException(SR.QueryServiceResultSetAddNoRows); + } + + StorageDataReader dataReader = new StorageDataReader(dbDataReader); + + using (IFileStreamWriter writer = fileStreamFactory.GetWriter(outputFileName)) + { + // Write the row to the end of the file + long currentFileOffset = totalBytesWritten; + writer.Seek(currentFileOffset); + await dataReader.ReadAsync(CancellationToken.None); + totalBytesWritten += writer.WriteRow(dataReader); + return currentFileOffset; + } + } + #endregion } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs index 733ada2e..18c69f80 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/SqlScriptFormatter.cs @@ -185,8 +185,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility // Make sure we have numeric precision and numeric scale if (!column.NumericPrecision.HasValue || !column.NumericScale.HasValue) { - // @TODO Move to constants - throw new InvalidOperationException("Decimal column is missing numeric precision or numeric scale"); + throw new InvalidOperationException(SR.SqlScriptFormatterDecimalMissingPrecision); } // Convert the value to a decimal, then convert that to a string diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs index 29a98058..647575fe 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Data.Common; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; +using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData @@ -26,7 +27,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I attempt to create a CellUpdate with a null string value // Then: I should get an exception thrown - Assert.Throws(() => new CellUpdate(new CellUpdateTestDbColumn(null), null)); + Assert.Throws(() => new CellUpdate(GetWrapper("ntext"), null)); } [Fact] @@ -34,7 +35,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I attempt to create a CellUpdate to set it to NULL (with mixed cases) const string nullString = "NULL"; - DbColumn col = new CellUpdateTestDbColumn(typeof(string)); + DbColumnWrapper col = GetWrapper("ntext"); CellUpdate cu = new CellUpdate(col, nullString); // Then: The value should be a DBNull and the string value should be the same as what @@ -49,7 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData public void NullTextStringTest() { // If: I attempt to create a CellUpdate with the text 'NULL' (with mixed case) - DbColumn col = new CellUpdateTestDbColumn(typeof(string)); + DbColumnWrapper col = GetWrapper("ntext"); CellUpdate cu = new CellUpdate(col, "'NULL'"); // Then: The value should be NULL @@ -64,7 +65,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData public void ByteArrayTest(string strValue, byte[] expectedValue, string expectedString) { // If: I attempt to create a CellUpdate for a binary column - DbColumn col = new CellUpdateTestDbColumn(typeof(byte[])); + DbColumnWrapper col = GetWrapper("binary"); CellUpdate cu = new CellUpdate(col, strValue); // Then: The value should be a binary and should match the expected data @@ -118,7 +119,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I attempt to create a CellUpdate for a binary column // Then: It should throw an exception - DbColumn col = new CellUpdateTestDbColumn(typeof(byte[])); + DbColumnWrapper col = GetWrapper("binary"); Assert.Throws(() => new CellUpdate(col, "this is totally invalid")); } @@ -127,7 +128,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData public void BoolTest(string input, bool output, string outputString) { // If: I attempt to create a CellUpdate for a boolean column - DbColumn col = new CellUpdateTestDbColumn(typeof(bool)); + DbColumnWrapper col = GetWrapper("bit"); CellUpdate cu = new CellUpdate(col, input); // Then: The value should match what was expected @@ -153,23 +154,22 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I create a CellUpdate for a bool column and provide an invalid numeric value // Then: It should throw an exception - DbColumn col = new CellUpdateTestDbColumn(typeof(bool)); + DbColumnWrapper col = GetWrapper("bit"); Assert.Throws(() => new CellUpdate(col, "12345")); } [Theory] [MemberData(nameof(RoundTripTestParams))] - public void RoundTripTest(Type dbColType, object obj) + public void RoundTripTest(DbColumnWrapper col, object obj) { // Setup: Figure out the test string string testString = obj.ToString(); - // If: I attempt to create a CellUpdate for a GUID column - DbColumn col = new CellUpdateTestDbColumn(dbColType); + // If: I attempt to create a CellUpdate CellUpdate cu = new CellUpdate(col, testString); // Then: The value and type should match what we put in - Assert.IsType(dbColType, cu.Value); + Assert.IsType(col.DataType, cu.Value); Assert.Equal(obj, cu.Value); Assert.Equal(testString, cu.ValueAsString); Assert.Equal(col, cu.Column); @@ -179,29 +179,35 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { get { - yield return new object[] {typeof(Guid), Guid.NewGuid()}; - yield return new object[] {typeof(TimeSpan), new TimeSpan(0, 1, 20, 0, 123)}; - yield return new object[] {typeof(DateTime), new DateTime(2016, 04, 25, 9, 45, 0)}; + yield return new object[] {GetWrapper("uniqueidentifier"), Guid.NewGuid()}; + yield return new object[] {GetWrapper("time"), new TimeSpan(0, 1, 20, 0, 123)}; + yield return new object[] {GetWrapper("datetime"), new DateTime(2016, 04, 25, 9, 45, 0)}; yield return new object[] { - typeof(DateTimeOffset), + GetWrapper("datetimeoffset"), new DateTimeOffset(2016, 04, 25, 9, 45, 0, TimeSpan.FromHours(8)) }; - yield return new object[] {typeof(long), 1000L}; - yield return new object[] {typeof(decimal), new decimal(3.14)}; - yield return new object[] {typeof(int), 1000}; - yield return new object[] {typeof(short), (short) 1000}; - yield return new object[] {typeof(byte), (byte) 5}; - yield return new object[] {typeof(double), 3.14d}; - yield return new object[] {typeof(float), 3.14f}; + yield return new object[] {GetWrapper("bigint"), 1000L}; + yield return new object[] {GetWrapper("decimal"), new decimal(3.14)}; + yield return new object[] {GetWrapper("int"), 1000}; + yield return new object[] {GetWrapper("smallint"), (short) 1000}; + yield return new object[] {GetWrapper("tinyint"), (byte) 5}; + yield return new object[] {GetWrapper("float"), 3.14d}; + yield return new object[] {GetWrapper("real"), 3.14f}; } } + private static DbColumnWrapper GetWrapper(string dataTypeName) + { + return new DbColumnWrapper(new CellUpdateTestDbColumn(typeof(T), dataTypeName)); + } + private class CellUpdateTestDbColumn : DbColumn { - public CellUpdateTestDbColumn(Type dataType) + public CellUpdateTestDbColumn(Type dataType, string dataTypeName) { DataType = dataType; + DataTypeName = dataTypeName; } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs index 864ab8b8..7665bf67 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/Common.cs @@ -69,18 +69,27 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData return columns.ToArray(); } - public static ResultSet GetResultSet(DbColumn[] columns, bool includeIdentity) + public static ResultSet GetResultSet(DbColumn[] columns, bool includeIdentity, int rowCount = 1) { - object[][] rows = includeIdentity - ? new[] { new object[] { "id", "1", "2", "3" } } - : new[] { new object[] { "1", "2", "3" } }; + IEnumerable rows = includeIdentity + ? Enumerable.Repeat(new object[] { "id", "1", "2", "3" }, rowCount) + : Enumerable.Repeat(new object[] { "1", "2", "3" }, rowCount); var testResultSet = new TestResultSet(columns, rows); var reader = new TestDbDataReader(new[] { testResultSet }); - var resultSet = new ResultSet(reader, 0, 0, MemoryFileSystem.GetFileStreamFactory()); - resultSet.ReadResultToEnd(CancellationToken.None).Wait(); + var resultSet = new ResultSet(0, 0, MemoryFileSystem.GetFileStreamFactory()); + resultSet.ReadResultToEnd(reader, CancellationToken.None).Wait(); return resultSet; } + public static DbDataReader GetNewRowDataReader(DbColumn[] columns, bool includeIdentity) + { + object[][] rows = includeIdentity + ? new[] {new object[] {"id", "q", "q", "q"}} + : new[] {new object[] {"q", "q", "q"}}; + var testResultSet = new TestResultSet(columns, rows); + return new TestDbDataReader(new [] {testResultSet}); + } + public static void AddCells(RowEditBase rc, bool includeIdentity) { // Skip the first column since if identity, since identity columns can't be updated diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs index 1b96063f..793a88ce 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs @@ -6,9 +6,11 @@ using System; using System.Data.Common; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; using Microsoft.SqlTools.ServiceLayer.QueryExecution; +using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData @@ -80,5 +82,110 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData RowCreate rc = new RowCreate(rowId, rs, etm); Assert.Throws(() => rc.GetScript()); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ApplyChanges(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.GetMetadata(columns); + + // ... Setup a db reader for the result of an insert + var newRowReader = Common.GetNewRowDataReader(columns, includeIdentity); + + // If: I ask for the change to be applied + RowCreate rc = new RowCreate(rowId, rs, etm); + await rc.ApplyChanges(newRowReader); + + // Then: The result set should have an additional row in it + Assert.Equal(2, rs.RowCount); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void 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 etm = Common.GetMetadata(columns); + RowCreate rc = new RowCreate(rowId, rs, etm); + Common.AddCells(rc, includeIdentity); + + // ... Mock db connection for building the command + var mockConn = new TestSqlConnection(null); + + // If: I attempt to get a command for the edit + DbCommand cmd = rc.GetCommand(mockConn); + + // Then: + // ... The command should not be null + Assert.NotNull(cmd); + + // ... There should be parameters in it + Assert.Equal(3, cmd.Parameters.Count); + + // ... It should be formatted into an insert script with output + Regex r = new Regex(@"INSERT INTO (.+)\((.+)\) OUTPUT (.+) VALUES \((.+)\)"); + var m = r.Match(cmd.CommandText); + Assert.True(m.Success); + + // ... There should be a table + string tbl = m.Groups[1].Value; + Assert.Equal(etm.EscapedMultipartName, tbl); + + // ... There should be 3 columns for input + string inCols = m.Groups[2].Value; + Assert.Equal(3, inCols.Split(',').Length); + + // ... There should be 3 OR 4 columns for output that are inserted. + string[] outCols = m.Groups[3].Value.Split(','); + Assert.Equal(includeIdentity ? 4 : 3, outCols.Length); + Assert.All(outCols, s => Assert.StartsWith("inserted.", s.Trim())); + + // ... There should be 3 parameters + string[] param = m.Groups[4].Value.Split(','); + Assert.Equal(3, param.Length); + Assert.All(param, s => Assert.StartsWith("@Value", s.Trim())); + } + + [Fact] + public void GetCommandNullConnection() + { + // Setup: Create a row create + const long rowId = 100; + var columns = Common.GetColumns(false); + var rs = Common.GetResultSet(columns, false); + var etm = Common.GetMetadata(columns); + RowCreate rc = new RowCreate(rowId, rs, etm); + + // If: I attempt to create a command with a null connection + // Then: It should throw an exception + Assert.Throws(() => rc.GetCommand(null)); + } + + [Fact] + public void GetCommandMissingCell() + { + // Setup: Generate the parameters for the row create + const long rowId = 100; + var columns = Common.GetColumns(false); + var rs = Common.GetResultSet(columns, false); + var etm = Common.GetMetadata(columns); + var mockConn = new TestSqlConnection(null); + + // If: I ask for a script to be generated without setting any values + // Then: An exception should be thrown for missing cells + RowCreate rc = new RowCreate(rowId, rs, etm); + Assert.Throws(() => rc.GetCommand(mockConn)); + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs index 6ebceba3..321ed582 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs @@ -5,9 +5,12 @@ using System; using System.Data.Common; +using System.Text.RegularExpressions; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; using Microsoft.SqlTools.ServiceLayer.QueryExecution; +using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData @@ -34,11 +37,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData [Theory] [InlineData(true)] [InlineData(false)] - public void GetScriptTest(bool isHekaton) + public void GetScriptTest(bool isMemoryOptimized) { DbColumn[] columns = Common.GetColumns(true); ResultSet rs = Common.GetResultSet(columns, true); - IEditTableMetadata etm = Common.GetMetadata(columns, false, isHekaton); + IEditTableMetadata etm = Common.GetMetadata(columns, false, isMemoryOptimized); // If: I ask for a script to be generated for delete RowDelete rd = new RowDelete(0, rs, etm); @@ -50,13 +53,94 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // ... It should be formatted as a delete script string scriptStart = $"DELETE FROM {etm.EscapedMultipartName}"; - if (isHekaton) + if (isMemoryOptimized) { scriptStart += " WITH(SNAPSHOT)"; } Assert.StartsWith(scriptStart, script); } + [Fact] + public async Task ApplyChanges() + { + // Setup: Generate the parameters for the row delete object + // We don't care about the values besides the row ID + const long rowId = 0; + var columns = Common.GetColumns(false); + var rs = Common.GetResultSet(columns, false); + var etm = Common.GetMetadata(columns); + + // If: I ask for the change to be applied + RowDelete rd = new RowDelete(rowId, rs, etm); + await rd.ApplyChanges(null); // Reader not used, can be null + + // Then : The result set should have one less row in it + Assert.Equal(0, rs.RowCount); + } + + [Theory] + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public void GetCommand(bool includeIdentity, bool isMemoryOptimized) + { + // Setup: + // ... Create a row delete + const long rowId = 0; + var columns = Common.GetColumns(includeIdentity); + var rs = Common.GetResultSet(columns, includeIdentity); + var etm = Common.GetMetadata(columns, !includeIdentity, isMemoryOptimized); + RowDelete rd = new RowDelete(rowId, rs, etm); + + // ... Mock db connection for building the command + var mockConn = new TestSqlConnection(null); + + // If: I attempt to get a command for the edit + DbCommand cmd = rd.GetCommand(mockConn); + + // Then: + // ... The command should not be null + Assert.NotNull(cmd); + + // ... Only the keys should be used for parameters + int expectedKeys = includeIdentity ? 1 : 3; + Assert.Equal(expectedKeys, cmd.Parameters.Count); + + // ... It should be formatted into an delete script + string regexTest = isMemoryOptimized + ? @"DELETE FROM (.+) WITH\(SNAPSHOT\) WHERE (.+)" + : @"DELETE FROM (.+) WHERE (.+)"; + Regex r = new Regex(regexTest); + var m = r.Match(cmd.CommandText); + Assert.True(m.Success); + + // ... There should be a table + string tbl = m.Groups[1].Value; + Assert.Equal(etm.EscapedMultipartName, tbl); + + // ... There should be as many where components as there are keys + string[] whereComponents = m.Groups[2].Value.Split(new[] {"AND"}, StringSplitOptions.None); + Assert.Equal(expectedKeys, whereComponents.Length); + + // ... Each component should have be equal to a parameter + Assert.All(whereComponents, c => Assert.True(Regex.IsMatch(c.Trim(), @"\(.+ = @.+\)"))); + } + + [Fact] + public void GetCommandNullConnection() + { + // Setup: Create a row delete + var columns = Common.GetColumns(false); + var rs = Common.GetResultSet(columns, false); + var etm = Common.GetMetadata(columns); + RowDelete rd = new RowDelete(0, rs, etm); + + // If: I attempt to create a command with a null connection + // Then: It should throw an exception + Assert.Throws(() => rd.GetCommand(null)); + } + [Fact] public void SetCell() { diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs index ff59c69b..1b98d16c 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs @@ -9,6 +9,7 @@ using System.Data.Common; using System.Linq; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData; using Microsoft.SqlTools.ServiceLayer.EditData.Contracts; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; @@ -99,13 +100,104 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData rt.ValidateWhereClauseNoKeys(); } + [Fact] + public void SortingByTypeTest() + { + // Setup: Create a result set and metadata we can reuse + var cols = Common.GetColumns(false); + var rs = Common.GetResultSet(cols, false); + var etm = Common.GetMetadata(cols); + + // If: I request to sort a list of the three different edit operations + List rowEdits = new List + { + new RowDelete(0, rs, etm), + new RowUpdate(0, rs, etm), + new RowCreate(0, rs, etm) + }; + rowEdits.Sort(); + + // Then: Delete should be the last operation to execute + // (we don't care about the order of the other two) + Assert.IsType(rowEdits.Last()); + } + + [Fact] + public void SortingUpdatesByRowIdTest() + { + // Setup: Create a result set and metadata we can reuse + var cols = Common.GetColumns(false); + var rs = Common.GetResultSet(cols, false, 4); + var etm = Common.GetMetadata(cols); + + // If: I sort 3 edit operations of the same type + List rowEdits = new List + { + new RowUpdate(3, rs, etm), + new RowUpdate(1, rs, etm), + new RowUpdate(2, rs, etm) + }; + rowEdits.Sort(); + + // Then: They should be in order by row ID ASCENDING + Assert.Equal(1, rowEdits[0].RowId); + Assert.Equal(2, rowEdits[1].RowId); + Assert.Equal(3, rowEdits[2].RowId); + } + + [Fact] + public void SortingCreatesByRowIdTest() + { + // Setup: Create a result set and metadata we can reuse + var cols = Common.GetColumns(false); + var rs = Common.GetResultSet(cols, false); + var etm = Common.GetMetadata(cols); + + // If: I sort 3 edit operations of the same type + List rowEdits = new List + { + new RowCreate(3, rs, etm), + new RowCreate(1, rs, etm), + new RowCreate(2, rs, etm) + }; + rowEdits.Sort(); + + // Then: They should be in order by row ID ASCENDING + Assert.Equal(1, rowEdits[0].RowId); + Assert.Equal(2, rowEdits[1].RowId); + Assert.Equal(3, rowEdits[2].RowId); + } + + [Fact] + public void SortingDeletesByRowIdTest() + { + // Setup: Create a result set and metadata we can reuse + var cols = Common.GetColumns(false); + var rs = Common.GetResultSet(cols, false); + var etm = Common.GetMetadata(cols); + + // If: I sort 3 delete operations of the same type + List rowEdits = new List + { + new RowDelete(1, rs, etm), + new RowDelete(3, rs, etm), + new RowDelete(2, rs, etm) + }; + rowEdits.Sort(); + + // Then: They should be in order by row ID DESCENDING + Assert.Equal(3, rowEdits[0].RowId); + Assert.Equal(2, rowEdits[1].RowId); + Assert.Equal(1, rowEdits[2].RowId); + } + private static 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(testReader, 0,0, MemoryFileSystem.GetFileStreamFactory()); - resultSet.ReadResultToEnd(CancellationToken.None).Wait(); + var resultSet = new ResultSet(0,0, MemoryFileSystem.GetFileStreamFactory()); + resultSet.ReadResultToEnd(testReader, CancellationToken.None).Wait(); return resultSet; } @@ -179,6 +271,18 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { throw new NotImplementedException(); } + + public override Task ApplyChanges(DbDataReader reader) + { + throw new NotImplementedException(); + } + + public override DbCommand GetCommand(DbConnection conn) + { + throw new NotImplementedException(); + } + + protected override int SortId => 0; } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs index 8d1e73a5..f6abfb3b 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs @@ -3,11 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; using System.Data.Common; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.EditData; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; using Microsoft.SqlTools.ServiceLayer.QueryExecution; +using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData @@ -69,12 +72,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData [Theory] [InlineData(true)] [InlineData(false)] - public void GetScriptTest(bool isHekaton) + public void GetScriptTest(bool isMemoryOptimized) { // Setup: Create a fake table to update DbColumn[] columns = Common.GetColumns(true); ResultSet rs = Common.GetResultSet(columns, true); - IEditTableMetadata etm = Common.GetMetadata(columns, false, isHekaton); + IEditTableMetadata etm = Common.GetMetadata(columns, false, isMemoryOptimized); // If: I ask for a script to be generated for update RowUpdate ru = new RowUpdate(0, rs, etm); @@ -86,7 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Assert.NotNull(script); // ... It should be formatted as an update script - string regexString = isHekaton + string regexString = isMemoryOptimized ? @"UPDATE (.+) WITH \(SNAPSHOT\) SET (.*) WHERE .+" : @"UPDATE (.+) SET (.*) WHERE .+"; Regex r = new Regex(regexString); @@ -101,5 +104,117 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Assert.Equal(3, updateSplit.Length); Assert.All(updateSplit, s => Assert.Equal(2, s.Split('=').Length)); } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void 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.GetMetadata(columns, !includeIdentity, isMemoryOptimized); + RowUpdate ru = new RowUpdate(0, rs, etm); + Common.AddCells(ru, includeIdentity); + + // ... Mock db connection for building the command + var mockConn = new TestSqlConnection(null); + + // If: I ask for a command to be generated for update + DbCommand cmd = ru.GetCommand(mockConn); + + // Then: + // ... The command should not be null + Assert.NotNull(cmd); + + // ... There should be an appropriate number of parameters in it + // (1 or 3 keys, 3 value parameters) + int expectedKeys = includeIdentity ? 1 : 3; + Assert.Equal(expectedKeys + 3, cmd.Parameters.Count); + + // ... It should be formatted into an update script with output + string regexFormat = isMemoryOptimized + ? @"UPDATE (.+) WITH \(SNAPSHOT\) SET (.+) OUTPUT (.+) WHERE (.+)" + : @"UPDATE (.+) SET (.+) OUTPUT(.+) WHERE (.+)"; + Regex r = new Regex(regexFormat); + var m = r.Match(cmd.CommandText); + Assert.True(m.Success); + + // ... There should be a table + string tbl = m.Groups[1].Value; + Assert.Equal(etm.EscapedMultipartName, tbl); + + // ... There should be 3 parameters for input + string[] inCols = m.Groups[2].Value.Split(','); + Assert.Equal(3, inCols.Length); + Assert.All(inCols, s => Assert.Matches(@"\[.+\] = @Value\d+", s)); + + // ... There should be 3 OR 4 columns for output + string[] outCols = m.Groups[3].Value.Split(','); + Assert.Equal(includeIdentity ? 4 : 3, outCols.Length); + Assert.All(outCols, s => Assert.StartsWith("inserted.", s.Trim())); + + // ... There should be 1 OR 3 columns for where components + string[] whereComponents = m.Groups[4].Value.Split(new[] {"AND"}, StringSplitOptions.None); + Assert.Equal(expectedKeys, whereComponents.Length); + Assert.All(whereComponents, s => Assert.Matches(@"\(.+ = @Param\d+\)", s)); + } + + [Fact] + public void GetCommandNullConnection() + { + // Setup: Create a row create + var columns = Common.GetColumns(false); + var rs = Common.GetResultSet(columns, false); + var etm = Common.GetMetadata(columns); + RowUpdate rc = new RowUpdate(0, rs, etm); + + // If: I attempt to create a command with a null connection + // Then: It should throw an exception + Assert.Throws(() => rc.GetCommand(null)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ApplyChanges(bool includeIdentity) + { + // Setup: + // ... Create a row update (no cell updates needed) + var columns = Common.GetColumns(includeIdentity); + var rs = Common.GetResultSet(columns, includeIdentity); + var etm = Common.GetMetadata(columns, !includeIdentity); + RowUpdate ru = new RowUpdate(0, rs, etm); + long oldBytesWritten = rs.totalBytesWritten; + + // ... Setup a db reader for the result of an update + var newRowReader = Common.GetNewRowDataReader(columns, includeIdentity); + + // If: I ask for the change to be applied + await ru.ApplyChanges(newRowReader); + + // Then: + // ... The result set should have the same number of rows as before + Assert.Equal(1, rs.RowCount); + Assert.True(oldBytesWritten < rs.totalBytesWritten); + } + + [Fact] + public async Task ApplyChangesNullReader() + { + // Setup: + // ... Create a row update (no cell updates needed) + var columns = Common.GetColumns(true); + var rs = Common.GetResultSet(columns, true); + var etm = Common.GetMetadata(columns, false); + RowUpdate ru = new RowUpdate(0, rs, etm); + + // If: I ask for the changes to be applied with a null db reader + // Then: I should get an exception + await Assert.ThrowsAsync(() => ru.ApplyChanges(null)); + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs index 80310b05..f820a3dd 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { public class ServiceIntegrationTests { - #region Session Operation Helper Tests + #region EditSession Operation Helper Tests [Theory] [InlineData(null)] @@ -128,7 +128,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData efv.Validate(); // ... There should be a delete in the session - Session s = eds.ActiveSessions[Constants.OwnerUri]; + EditSession s = eds.ActiveSessions[Constants.OwnerUri]; Assert.True(s.EditCache.Any(e => e.Value is RowDelete)); } @@ -150,7 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData efv.Validate(); // ... There should be a create in the session - Session s = eds.ActiveSessions[Constants.OwnerUri]; + EditSession s = eds.ActiveSessions[Constants.OwnerUri]; Assert.True(s.EditCache.Any(e => e.Value is RowCreate)); } @@ -174,7 +174,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData efv.Validate(); // ... The edit cache should be empty again - Session s = eds.ActiveSessions[Constants.OwnerUri]; + EditSession s = eds.ActiveSessions[Constants.OwnerUri]; Assert.Empty(s.EditCache); } @@ -215,13 +215,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData edit.Verify(e => e.SetCell(It.IsAny(), It.IsAny()), Times.Once); } - private static Session GetDefaultSession() + private static 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.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); return s; } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs index 42643a25..4f02f781 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Data.Common; using System.IO; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.EditData; using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement; @@ -28,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I create a session object without a null query // Then: It should throw an exception - Assert.Throws(() => new Session(null, Common.GetMetadata(new DbColumn[] {}))); + Assert.Throws(() => new EditSession(null, Common.GetMetadata(new DbColumn[] {}))); } [Fact] @@ -38,7 +39,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Then: It should throw an exception Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; - Assert.Throws(() => new Session(rs, null)); + Assert.Throws(() => new EditSession(rs, null)); } [Fact] @@ -48,12 +49,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // Then: // ... The edit cache should exist and be empty Assert.NotNull(s.EditCache); Assert.Empty(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); @@ -70,7 +72,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // Then: It should throw an exception Query q = QueryExecution.Common.GetBasicExecutedQuery(); q.HasExecuted = false; - Assert.Throws(() => Session.ValidateQueryForSession(q)); + Assert.Throws(() => EditSession.ValidateQueryForSession(q)); } [Fact] @@ -94,7 +96,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData // If: I create a session object with a query that has !=1 result sets // Then: It should throw an exception - Assert.Throws(() => Session.ValidateQueryForSession(query)); + Assert.Throws(() => EditSession.ValidateQueryForSession(query)); } [Fact] @@ -102,7 +104,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData { // If: I validate a query for a session with a valid query Query q = QueryExecution.Common.GetBasicExecutedQuery(); - ResultSet rs = Session.ValidateQueryForSession(q); + ResultSet rs = EditSession.ValidateQueryForSession(q); // Then: I should get the only result set back Assert.NotNull(rs); @@ -121,7 +123,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail var mockEdit = new Mock().Object; @@ -146,7 +148,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // If: I add a row to the session long newId = s.CreateRow(); @@ -167,13 +169,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData [Theory] [MemberData(nameof(RowIdOutOfRangeData))] - public void RowIdOutOfRange(long rowId, Action testAction) + public void RowIdOutOfRange(long rowId, Action 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.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // If: I delete a row that is out of range for the result set // Then: I should get an exception @@ -185,12 +187,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData get { // Delete Row - Action delAction = (s, l) => s.DeleteRow(l); + Action delAction = (s, l) => s.DeleteRow(l); yield return new object[] { -1L, delAction }; yield return new object[] { 100L, delAction }; // Update Cell - Action upAction = (s, l) => s.UpdateCell(l, 0, null); + Action upAction = (s, l) => s.UpdateCell(l, 0, null); yield return new object[] { -1L, upAction }; yield return new object[] { 100L, upAction }; } @@ -206,7 +208,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail var mockEdit = new Mock().Object; @@ -228,7 +230,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // If: I add a row to the session s.DeleteRow(0); @@ -249,7 +251,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // If: I revert a row that doesn't have any pending changes // Then: I should get an exception @@ -264,7 +266,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail var mockEdit = new Mock().Object; @@ -290,7 +292,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // ... Add a mock edit to the edit cache to cause the .TryAdd to fail var mockEdit = new Mock(); @@ -314,7 +316,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // If: I update a cell on a row that does not have a pending edit s.UpdateCell(0, 0, ""); @@ -339,7 +341,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // If: I try to script the edit cache with a null or whitespace output path // Then: It should throw an exception @@ -354,7 +356,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData Query q = QueryExecution.Common.GetBasicExecutedQuery(); ResultSet rs = q.Batches[0].ResultSets[0]; IEditTableMetadata etm = Common.GetMetadata(rs.Columns); - Session s = new Session(rs, etm); + EditSession s = new EditSession(rs, etm); // ... Add two mock edits that will generate a script Mock edit = new Mock(); @@ -377,5 +379,163 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData } #endregion + + #region Commit Tests + + [Fact] + public void CommitNullConnection() + { + // Setup: Create a basic session + EditSession s = GetBasicSession(); + + // If: I attempt to commit with a null connection + // Then: I should get an exception + Assert.Throws( + () => s.CommitEdits(null, () => Task.CompletedTask, e => Task.CompletedTask)); + } + + [Fact] + public void CommitNullSuccessHandler() + { + // Setup: + // ... Create a basic session + EditSession s = GetBasicSession(); + + // ... Mock db connection + DbConnection conn = new TestSqlConnection(null); + + // If: I attempt to commit with a null success handler + // Then: I should get an exception + Assert.Throws(() => s.CommitEdits(conn, null, e => Task.CompletedTask)); + } + + [Fact] + public void CommitNullFailureHandler() + { + // Setup: + // ... Create a basic session + EditSession s = GetBasicSession(); + + // ... Mock db connection + DbConnection conn = new TestSqlConnection(null); + + // If: I attempt to commit with a null success handler + // Then: I should get an exception + Assert.Throws(() => s.CommitEdits(conn, () => Task.CompletedTask, null)); + } + + [Fact] + public void CommitInProgress() + { + // Setup: + // ... Basic session and db connection + EditSession s = GetBasicSession(); + DbConnection conn = new TestSqlConnection(null); + + // ... Mock a task that has not completed + Task notCompleted = new Task(() => {}); + s.CommitTask = notCompleted; + + // If: I attempt to commit while a task is in progress + // Then: I should get an exception + Assert.Throws( + () => s.CommitEdits(conn, () => Task.CompletedTask, e => Task.CompletedTask)); + } + + [Fact] + public async Task CommitSuccess() + { + // Setup: + // ... Basic session and db connection + EditSession s = GetBasicSession(); + DbConnection conn = new TestSqlConnection(null); + + // ... Add a mock commands for fun + Mock edit = new Mock(); + edit.Setup(e => e.GetCommand(It.IsAny())).Returns(dbc => dbc.CreateCommand()); + edit.Setup(e => e.ApplyChanges(It.IsAny())).Returns(Task.FromResult(0)); + s.EditCache[0] = edit.Object; + + // If: I commit these changes (and await completion) + bool successCalled = false; + bool failureCalled = false; + s.CommitEdits(conn, + () => { + successCalled = true; + return Task.FromResult(0); + }, + e => { + failureCalled = true; + return Task.FromResult(0); + }); + await s.CommitTask; + + // Then: + // ... The task should still exist + Assert.NotNull(s.CommitTask); + + // ... The success handler should have been called (not failure) + Assert.True(successCalled); + Assert.False(failureCalled); + + // ... The mock edit should have generated a command and applied changes + edit.Verify(e => e.GetCommand(conn), Times.Once); + edit.Verify(e => e.ApplyChanges(It.IsAny()), Times.Once); + + // ... The edit cache should be empty + Assert.Empty(s.EditCache); + } + + [Fact] + public async Task CommitFailure() + { + // Setup: + // ... Basic session and db connection + EditSession s = GetBasicSession(); + DbConnection conn = new TestSqlConnection(null); + + // ... Add a mock edit that will explode on generating a command + Mock edit = new Mock(); + edit.Setup(e => e.GetCommand(It.IsAny())).Throws(); + s.EditCache[0] = edit.Object; + + // If: I commit these changes (and await completion) + bool successCalled = false; + bool failureCalled = false; + s.CommitEdits(conn, + () => { + successCalled = true; + return Task.FromResult(0); + }, + e => { + failureCalled = true; + return Task.FromResult(0); + }); + await s.CommitTask; + + // Then: + // ... The task should still exist + Assert.NotNull(s.CommitTask); + + // ... The error handler should have been called (not success) + Assert.False(successCalled); + Assert.True(failureCalled); + + // ... The mock edit should have been asked to generate a command + edit.Verify(e => e.GetCommand(conn), Times.Once); + + // ... The edit cache should not be empty + Assert.NotEmpty(s.EditCache); + } + + #endregion + + private static EditSession GetBasicSession() + { + Query q = QueryExecution.Common.GetBasicExecutedQuery(); + ResultSet rs = q.Batches[0].ResultSets[0]; + IEditTableMetadata etm = Common.GetMetadata(rs.Columns); + return new EditSession(rs, etm); + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DisposeTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DisposeTests.cs index 4720c38a..61efd556 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DisposeTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DisposeTests.cs @@ -24,10 +24,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution { // Setup: Mock file stream factory, mock db reader var mockFileStreamFactory = new Mock(); - var mockDataReader = Common.CreateTestConnection(null, false).CreateCommand().ExecuteReaderAsync().Result; // If: I setup a single resultset and then dispose it - ResultSet rs = new ResultSet(mockDataReader, Common.Ordinal, Common.Ordinal, mockFileStreamFactory.Object); + ResultSet rs = new ResultSet(Common.Ordinal, Common.Ordinal, mockFileStreamFactory.Object); rs.Dispose(); // Then: The file that was created should have been deleted diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/Execution/ResultSetTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/Execution/ResultSetTests.cs index 722fe27e..2afc67cd 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/Execution/ResultSetTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/Execution/ResultSetTests.cs @@ -4,6 +4,7 @@ // using System; +using System.Collections.Generic; using System.Data.Common; using System.Linq; using System.Threading; @@ -24,8 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution { // If: // ... I create a new result set with a valid db data reader - DbDataReader mockReader = GetReader(null, false, string.Empty); - ResultSet resultSet = new ResultSet(mockReader, Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); // Then: // ... There should not be any data read yet @@ -41,14 +41,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution } [Fact] - public void ResultCreationInvalidReader() + public async Task ReadToEndNullReader() { - // If: - // ... I create a new result set without a reader - // Then: - // ... It should throw an exception - Assert.Throws(() => new ResultSet(null, Common.Ordinal, Common.Ordinal, null)); - + // If: I create a new result set with a null db data reader + // Then: I should get an exception + var fsf = MemoryFileSystem.GetFileStreamFactory(); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fsf); + await Assert.ThrowsAsync(() => resultSet.ReadResultToEnd(null, CancellationToken.None)); } [Fact] @@ -67,9 +66,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution // ... and I read it to the end DbDataReader mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); - ResultSet resultSet = new ResultSet(mockReader, Common.Ordinal, Common.Ordinal, fileStreamFactory); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); resultSet.ResultCompletion += callback; - await resultSet.ReadResultToEnd(CancellationToken.None); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); // Then: // ... The columns should be set @@ -86,7 +85,35 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution // ... The callback for result set completion should have been fired Assert.NotNull(resultSummaryFromCallback); } - + + [Theory] + [MemberData(nameof(CallMethodWithoutReadingData))] + public void CallMethodWithoutReading(Action testMethod) + { + // Setup: Create a new result set with valid db data reader + var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); + + // If: + // ... I have a result set that has not been read + // ... and I attempt to call a method on it + // Then: It should throw an exception + Assert.ThrowsAny(() => testMethod(resultSet)); + } + + public static IEnumerable CallMethodWithoutReadingData + { + get + { + yield return new object[] {new Action(rs => rs.GetSubset(0, 0).Wait())}; + yield return new object[] {new Action(rs => rs.UpdateRow(0, null).Wait())}; + yield return new object[] {new Action(rs => rs.AddRow(null).Wait())}; + yield return new object[] {new Action(rs => rs.RemoveRow(0))}; + yield return new object[] {new Action(rs => rs.GetRow(0))}; + yield return new object[] {new Action(rs => rs.GetExecutionPlan().Wait())}; + } + } + [Theory] [InlineData("JSON")] [InlineData("XML")] @@ -111,9 +138,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution // ... and I read it to the end DbDataReader mockReader = GetReader(dataSets, false, Constants.StandardQuery); var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); - ResultSet resultSet = new ResultSet(mockReader, Common.Ordinal, Common.Ordinal, fileStreamFactory); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); resultSet.ResultCompletion += callback; - await resultSet.ReadResultToEnd(CancellationToken.None); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); // Then: // ... There should only be one column @@ -133,20 +160,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution Assert.Equal(1, subset.RowCount); } - [Fact] - public async Task GetSubsetWithoutExecution() - { - // If: - // ... I create a new result set with a valid db data reader without executing it - DbDataReader mockReader = GetReader(null, false, string.Empty); - var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); - ResultSet resultSet = new ResultSet(mockReader, Common.Ordinal, Common.Ordinal, fileStreamFactory); - - // Then: - // ... Attempting to read a subset should fail miserably - await Assert.ThrowsAsync(() => resultSet.GetSubset(0, 0)); - } - [Theory] [InlineData(-1, 0)] // Too small start row [InlineData(20, 0)] // Too large start row @@ -158,8 +171,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution // ... And execute the result DbDataReader mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); - ResultSet resultSet = new ResultSet(mockReader, Common.Ordinal, Common.Ordinal, fileStreamFactory); - await resultSet.ReadResultToEnd(CancellationToken.None); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); // ... And attempt to get a subset with invalid parameters // Then: @@ -179,8 +192,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution // ... And execute the result set DbDataReader mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); - ResultSet resultSet = new ResultSet(mockReader, Common.Ordinal, Common.Ordinal, fileStreamFactory); - await resultSet.ReadResultToEnd(CancellationToken.None); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); // ... And attempt to get a subset with valid number of rows ResultSetSubset subset = await resultSet.GetSubset(startRow, rowCount); @@ -195,6 +208,159 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution Assert.Equal(resultSet.Columns.Length, subset.Rows[0].Length); } + [Theory] + [MemberData(nameof(RowInvalidParameterData))] + public async Task RowInvalidParameter(Action actionToPerform) + { + // If: I create a new result set and execute it + var mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); + var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileStreamFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); + + // Then: Attempting to read an invalid row should fail + Assert.ThrowsAny(() => actionToPerform(resultSet)); + } + + public static IEnumerable RowInvalidParameterData + { + get + { + foreach (var method in RowInvalidParameterMethods) + { + yield return new object[] {new Action(rs => method(rs, -1))}; + yield return new object[] {new Action(rs => method(rs, 100))}; + } + } + } + + public static IEnumerable> RowInvalidParameterMethods + { + get + { + yield return (rs, id) => rs.RemoveRow(id); + yield return (rs, id) => rs.GetRow(id); + yield return (rs, id) => rs.UpdateRow(id, null).Wait(); + } + } + + [Fact] + public async Task RemoveRowSuccess() + { + // Setup: Create a result set that has the standard data set on it + var fileFactory = MemoryFileSystem.GetFileStreamFactory(); + var mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); + + // If: I delete a row from the result set + resultSet.RemoveRow(0); + + // Then: + // ... The row count should decrease + // ... The last row should have moved up by 1 + Assert.Equal(Common.StandardRows - 1, resultSet.RowCount); + Assert.Throws(() => resultSet.GetRow(Common.StandardRows - 1)); + } + + [Fact] + public async Task AddRowNoRows() + { + // Setup: + // ... Create a standard result set with standard data + var fileFactory = MemoryFileSystem.GetFileStreamFactory(); + var mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); + + // ... Create a mock reader that has no rows + var emptyReader = GetReader(new[] {new TestResultSet(5, 0)}, false, Constants.StandardQuery); + + // If: I add a row with a reader that has no rows + // Then: + // ... I should get an exception + await Assert.ThrowsAsync(() => resultSet.AddRow(emptyReader)); + + // ... The row count should not have changed + Assert.Equal(Common.StandardRows, resultSet.RowCount); + } + + [Fact] + public async Task AddRowSuccess() + { + // Setup: + // ... Create a standard result set with standard data + var fileFactory = MemoryFileSystem.GetFileStreamFactory(); + var mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); + + // ... Create a mock reader that has one row + object[] row = Enumerable.Range(0, Common.StandardColumns).Select(i => "QQQ").ToArray(); + IEnumerable rows = new List{ row }; + TestResultSet[] results = {new TestResultSet(TestResultSet.GetStandardColumns(Common.StandardColumns), rows)}; + var newRowReader = GetReader(results, false, Constants.StandardQuery); + + // If: I add a new row to the result set + await resultSet.AddRow(newRowReader); + + // Then: + // ... There should be a new row in the list of rows + Assert.Equal(Common.StandardRows + 1, resultSet.RowCount); + + // ... The new row should be readable and all cells contain the test value + Assert.All(resultSet.GetRow(Common.StandardRows), cell => Assert.Equal("QQQ", cell.RawObject)); + } + + [Fact] + public async Task UpdateRowNoRows() + { + // Setup: + // ... Create a standard result set with standard data + var fileFactory = MemoryFileSystem.GetFileStreamFactory(); + var mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); + + // ... Create a mock reader that has no rows + var emptyReader = GetReader(new[] { new TestResultSet(5, 0) }, false, Constants.StandardQuery); + + // If: I add a row with a reader that has no rows + // Then: + // ... I should get an exception + await Assert.ThrowsAsync(() => resultSet.UpdateRow(0, emptyReader)); + + // ... The row count should not have changed + Assert.Equal(Common.StandardRows, resultSet.RowCount); + } + + [Fact] + public async Task UpdateRowSuccess() + { + // Setup: + // ... Create a standard result set with standard data + var fileFactory = MemoryFileSystem.GetFileStreamFactory(); + var mockReader = GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery); + ResultSet resultSet = new ResultSet(Common.Ordinal, Common.Ordinal, fileFactory); + await resultSet.ReadResultToEnd(mockReader, CancellationToken.None); + + // ... Create a mock reader that has one row + object[] row = Enumerable.Range(0, Common.StandardColumns).Select(i => "QQQ").ToArray(); + IEnumerable rows = new List { row }; + TestResultSet[] results = { new TestResultSet(TestResultSet.GetStandardColumns(Common.StandardColumns), rows) }; + var newRowReader = GetReader(results, false, Constants.StandardQuery); + + // If: I add a new row to the result set + await resultSet.UpdateRow(0, newRowReader); + + // Then: + // ... There should be the same number of rows + Assert.Equal(Common.StandardRows, resultSet.RowCount); + + // ... The new row should be readable and all cells contain the test value + Assert.All(resultSet.GetRow(0), cell => Assert.Equal("QQQ", cell.RawObject)); + } + private static DbDataReader GetReader(TestResultSet[] dataSet, bool throwOnRead, string query) { var info = Common.CreateTestConnectionInfo(dataSet, throwOnRead); diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SaveResults/ResultSetTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SaveResults/ResultSetTests.cs index 4aff9a60..2f1ea00e 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SaveResults/ResultSetTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SaveResults/ResultSetTests.cs @@ -27,7 +27,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults // If: I attempt to save with a null set of params // Then: I should get a null argument exception ResultSet rs = new ResultSet( - GetReader(null, false, Common.NoOpQuery), Common.Ordinal, Common.Ordinal, + Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); Assert.Throws(() => rs.SaveAs( null, @@ -41,7 +41,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults // If: I attempt to save with a null set of params // Then: I should get a null argument exception ResultSet rs = new ResultSet( - GetReader(null, false, Common.NoOpQuery), Common.Ordinal, Common.Ordinal, + Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); Assert.Throws(() => rs.SaveAs( new SaveResultsRequestParams(), @@ -54,7 +54,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults // If: I attempt to save a result set that hasn't completed execution // Then: I should get an invalid operation exception ResultSet rs = new ResultSet( - GetReader(null, false, Common.NoOpQuery), Common.Ordinal, Common.Ordinal, + Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); Assert.Throws(() => rs.SaveAs( new SaveResultsRequestParams(), @@ -68,7 +68,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults // Setup: // ... Create a result set that has been executed ResultSet rs = new ResultSet( - GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery), Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); @@ -92,10 +91,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults // ... Create a result set with dummy data and read to the end ResultSet rs = new ResultSet( - GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery), Common.Ordinal, Common.Ordinal, resultFactory); - await rs.ReadResultToEnd(CancellationToken.None); + await rs.ReadResultToEnd(GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery), CancellationToken.None); // ... Create a mock writer for writing the save as file Mock saveWriter = GetMockWriter(); @@ -125,10 +123,9 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.SaveResults // ... Create a result set with dummy data and read to the end ResultSet rs = new ResultSet( - GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery), Common.Ordinal, Common.Ordinal, resultFactory); - await rs.ReadResultToEnd(CancellationToken.None); + await rs.ReadResultToEnd(GetReader(Common.StandardTestDataSet, false, Constants.StandardQuery), CancellationToken.None); // ... Create a mock writer for writing the save as file Mock saveWriter = GetMockWriter(); diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs index 4b48a17c..60929b49 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs @@ -64,7 +64,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution // ... I have a resultset that hasn't been executed and I request a valid result set from it // Then: // ... It should throw an exception for having not been read - ResultSet rs = new ResultSet(new TestDbDataReader(null), Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); + ResultSet rs = new ResultSet(Common.Ordinal, Common.Ordinal, MemoryFileSystem.GetFileStreamFactory()); await Assert.ThrowsAsync(() => rs.GetSubset(0, 1)); } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbDataSet.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbDataSet.cs index 8235e09e..5f95af26 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbDataSet.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestDbDataSet.cs @@ -15,9 +15,14 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility public List Columns; public List Rows; + public static List GetStandardColumns(int columnCount) + { + return Enumerable.Range(0, columnCount).Select(i => new TestDbColumn($"Col{i}")).Cast().ToList(); + } + public TestResultSet(int columns, int rows) { - Columns = Enumerable.Range(0, columns).Select(i => new TestDbColumn($"Col{i}")).Cast().ToList(); + Columns = GetStandardColumns(columns); Rows = new List(rows); for (int i = 0; i < rows; i++) { diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestObjects.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestObjects.cs index da69daa4..ef405469 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestObjects.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/TestObjects.cs @@ -4,12 +4,15 @@ // using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Linq; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.LanguageServices; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; +using Moq; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility { @@ -98,6 +101,15 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility internal TestSqlCommand(TestResultSet[] data) { Data = data; + + var mockParameterCollection = new Mock(); + mockParameterCollection.Setup(c => c.Add(It.IsAny())) + .Callback(d => listParams.Add((DbParameter)d)); + mockParameterCollection.Setup(c => c.AddRange(It.IsAny())) + .Callback(d => listParams.AddRange(d.Cast())); + mockParameterCollection.Setup(c => c.Count) + .Returns(() => listParams.Count); + DbParameterCollection = mockParameterCollection.Object; } internal TestResultSet[] Data { get; set; } @@ -140,6 +152,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility { return new TestDbDataReader(Data); } + + private List listParams = new List(); } ///