diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteRequest.cs index 4d9bf3ba..7630b712 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteRequest.cs @@ -29,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts public class QueryExecuteResult { /// - /// Connection error messages. Optional, can be set to null to indicate no errors + /// Informational messages from the query runner. Optional, can be set to null. /// public string Messages { get; set; } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs index c97a566b..04705004 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs @@ -424,6 +424,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution OwnerUri = executeParams.OwnerUri, BatchSummaries = q.BatchSummaries }; + await requestContext.SendEvent(QueryExecuteCompleteEvent.Type, eventParams); }; @@ -470,9 +471,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution query.Execute(); // Send back a result showing we were successful + string messages = null; + if (query.Batches.Length == 0) + { + // If there were no batches to execute, send back an informational message that the commands were completed successfully + messages = SR.QueryServiceCompletedSuccessfully; + } await requestContext.SendResult(new QueryExecuteResult { - Messages = null + Messages = messages }); } diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/QueryExecutionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/QueryExecutionTests.cs index d57f91d9..462dd354 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/QueryExecutionTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/QueryExecutionTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.SqlTools.ServiceLayer; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.TestDriver.Utility; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; @@ -323,5 +324,36 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests await testHelper.Disconnect(queryTempFile.FilePath); } } + + [Fact] + public async Task NoOpQueryReturnsMessage() + { + // Given queries that do nothing (no-ops)... + var queries = new string[] + { + "-- no-op", + "GO", + "GO -- no-op" + }; + + using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) + using (TestHelper testHelper = new TestHelper()) + { + foreach (var query in queries) + { + Assert.True(await testHelper.Connect(queryTempFile.FilePath, ConnectionTestUtils.LocalhostConnection)); + + // If the queries are executed... + var queryResult = await testHelper.RunQueryAsync(queryTempFile.FilePath, query); + + // Then I expect messages that the commands were completed successfully to be in the result + Assert.NotNull(queryResult); + Assert.NotNull(queryResult.Messages); + Assert.Equal("Commands completed successfully.", queryResult.Messages); + + await testHelper.Disconnect(queryTempFile.FilePath); + } + } + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestHelper.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestHelper.cs index 7f675941..3f3906a2 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestHelper.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestHelper.cs @@ -272,6 +272,23 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests return null; } } + + /// + /// Run a query using a given connection bound to a URI. This method only waits for the initial response from query + /// execution (QueryExecuteResult). It is up to the caller to wait for the QueryExecuteCompleteEvent if they are interested. + /// + public async Task RunQueryAsync(string ownerUri, string query, int timeoutMilliseconds = 5000) + { + WriteToFile(ownerUri, query); + + var queryParams = new QueryExecuteParams + { + OwnerUri = ownerUri, + QuerySelection = null + }; + + return await Driver.SendRequest(QueryExecuteRequest.Type, queryParams); + } /// /// Request to cancel an executing query