diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
index b01fb258..7336cec2 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
@@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
this.outputFileFactory = outputFileFactory;
}
- #region Properties
+ #region Events
///
/// Asynchronous handler for when batches are completed
@@ -88,12 +88,21 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
///
public event BatchAsyncEventHandler BatchCompletion;
+ ///
+ /// Event to call when the batch has started execution
+ ///
+ public event BatchAsyncEventHandler BatchStart;
+
///
/// Event that will be called when the resultset has completed execution. It will not be
/// called from the Batch but from the ResultSet instance
///
public event ResultSet.ResultSetAsyncEventHandler ResultSetCompletion;
+ #endregion
+
+ #region Properties
+
///
/// The text of batch that will be executed
///
@@ -175,17 +184,25 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
get
{
- return new BatchSummary
+ // Batch summary with information available at start
+ BatchSummary summary = new BatchSummary
{
HasError = HasError,
Id = Id,
- ResultSetSummaries = ResultSummaries,
- Messages = ResultMessages.ToArray(),
Selection = Selection,
- ExecutionElapsed = ExecutionElapsedTime,
- ExecutionStart = ExecutionStartTimeStamp,
- ExecutionEnd = ExecutionEndTimeStamp
+ ExecutionStart = ExecutionStartTimeStamp
};
+
+ // Add on extra details if we finished executing it
+ if (HasExecuted)
+ {
+ summary.ResultSetSummaries = ResultSummaries;
+ summary.Messages = ResultMessages.ToArray();
+ summary.ExecutionEnd = ExecutionEndTimeStamp;
+ summary.ExecutionElapsed = ExecutionElapsedTime;
+ }
+
+ return summary;
}
}
@@ -211,6 +228,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
throw new InvalidOperationException("Batch has already executed.");
}
+ // Notify that we've started execution
+ if (BatchStart != null)
+ {
+ await BatchStart(this);
+ }
+
try
{
// Register the message listener to *this instance* of the batch
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteBatchCompleteNotification.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteBatchNotifications.cs
similarity index 65%
rename from src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteBatchCompleteNotification.cs
rename to src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteBatchNotifications.cs
index 6a1a6a6c..42877b6d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteBatchCompleteNotification.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/QueryExecuteBatchNotifications.cs
@@ -10,7 +10,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
/// Parameters to be sent back as part of a QueryExecuteBatchCompleteEvent to indicate that a
/// batch of a query completed.
///
- public class QueryExecuteBatchCompleteParams
+ public class QueryExecuteBatchNotificationParams
{
///
/// Summary of the batch that just completed
@@ -26,7 +26,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
public class QueryExecuteBatchCompleteEvent
{
public static readonly
- EventType Type =
- EventType.Create("query/batchComplete");
+ EventType Type =
+ EventType.Create("query/batchComplete");
+ }
+
+ public class QueryExecuteBatchStartEvent
+ {
+ public static readonly
+ EventType Type =
+ EventType.Create("query/batchStart");
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
index 120f4454..3c5bae8c 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
@@ -99,10 +99,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
///
public event Batch.BatchAsyncEventHandler BatchCompleted;
+ ///
+ /// Event to be called when a batch starts execution.
+ ///
+ public event Batch.BatchAsyncEventHandler BatchStarted;
+
///
/// Delegate type for callback when a query connection fails
///
- /// The query that completed
+ /// Error message for the failing query
public delegate Task QueryAsyncErrorEventHandler(string message);
///
@@ -277,6 +282,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// We need these to execute synchronously, otherwise the user will be very unhappy
foreach (Batch b in Batches)
{
+ b.BatchStart += BatchStarted;
b.BatchCompletion += BatchCompleted;
b.ResultSetCompletion += ResultSetCompleted;
await b.Execute(conn, cancellationSource.Token);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
index 04705004..4bd9f1fa 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
@@ -443,17 +443,28 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
query.QueryFailed += callback;
query.QueryConnectionException += errorCallback;
- // Setup the batch completion callback
- Batch.BatchAsyncEventHandler batchCallback = async b =>
+ // Setup the batch callbacks
+ Batch.BatchAsyncEventHandler batchStartCallback = async b =>
{
- QueryExecuteBatchCompleteParams eventParams = new QueryExecuteBatchCompleteParams
+ QueryExecuteBatchNotificationParams eventParams = new QueryExecuteBatchNotificationParams
+ {
+ BatchSummary = b.Summary,
+ OwnerUri = executeParams.OwnerUri
+ };
+ await requestContext.SendEvent(QueryExecuteBatchStartEvent.Type, eventParams);
+ };
+ query.BatchStarted += batchStartCallback;
+
+ Batch.BatchAsyncEventHandler batchCompleteCallback = async b =>
+ {
+ QueryExecuteBatchNotificationParams eventParams = new QueryExecuteBatchNotificationParams
{
BatchSummary = b.Summary,
OwnerUri = executeParams.OwnerUri
};
await requestContext.SendEvent(QueryExecuteBatchCompleteEvent.Type, eventParams);
};
- query.BatchCompleted += batchCallback;
+ query.BatchCompleted += batchCompleteCallback;
// Setup the ResultSet completion callback
ResultSet.ResultSetAsyncEventHandler resultCallback = async r =>
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/BatchTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/BatchTests.cs
index b5b0992a..12b99a3b 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/BatchTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/BatchTests.cs
@@ -46,22 +46,34 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... The summary should have the same info
Assert.False(batch.Summary.HasError);
Assert.Equal(Common.Ordinal, batch.Summary.Id);
- Assert.Empty(batch.Summary.ResultSetSummaries);
- Assert.Empty(batch.Summary.Messages);
+ Assert.Null(batch.Summary.ResultSetSummaries);
+ Assert.Null(batch.Summary.Messages);
Assert.Equal(0, batch.Summary.Selection.StartLine);
Assert.NotEqual(default(DateTime).ToString("o"), batch.Summary.ExecutionStart); // Should have been set at construction
- Assert.Equal(default(DateTime).ToString("o"), batch.Summary.ExecutionEnd);
- Assert.Equal((default(DateTime) - DateTime.Parse(batch.Summary.ExecutionStart)).ToString(), batch.Summary.ExecutionElapsed);
+ Assert.Null(batch.Summary.ExecutionEnd);
+ Assert.Null(batch.Summary.ExecutionElapsed);
}
+ ///
+ /// Note: This test also tests the start notification feature
+ ///
[Fact]
public void BatchExecuteNoResultSets()
{
- // Setup: Create a callback for batch completion
- BatchSummary batchSummaryFromCallback = null;
- Batch.BatchAsyncEventHandler batchCallback = b =>
+ // Setup:
+ // ... Create a callback for batch start
+ BatchSummary batchSummaryFromStart = null;
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
{
- batchSummaryFromCallback = b.Summary;
+ batchSummaryFromStart = b.Summary;
+ return Task.FromResult(0);
+ };
+
+ // ... Create a callback for batch completion
+ BatchSummary batchSummaryFromCompletion = null;
+ Batch.BatchAsyncEventHandler batchCompleteCallback = b =>
+ {
+ batchSummaryFromCompletion = b.Summary;
return Task.FromResult(0);
};
@@ -76,7 +88,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// If I execute a query that should get no result sets
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
- batch.BatchCompletion += batchCallback;
+ batch.BatchStart += batchStartCallback;
+ batch.BatchCompletion += batchCompleteCallback;
batch.ResultSetCompletion += resultSetCallback;
batch.Execute(GetConnection(Common.CreateTestConnectionInfo(null, false)), CancellationToken.None).Wait();
@@ -96,21 +109,32 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... There should be a message for how many rows were affected
Assert.Equal(1, batch.ResultMessages.Count());
+ // ... The callback for batch start should have been called
+ // ... The info from it should have been basic
+ Assert.NotNull(batchSummaryFromStart);
+ Assert.False(batchSummaryFromStart.HasError);
+ Assert.Equal(Common.Ordinal, batchSummaryFromStart.Id);
+ Assert.Equal(Common.SubsectionDocument, batchSummaryFromStart.Selection);
+ Assert.True(DateTime.Parse(batchSummaryFromStart.ExecutionStart) > default(DateTime));
+ Assert.Null(batchSummaryFromStart.ResultSetSummaries);
+ Assert.Null(batchSummaryFromStart.Messages);
+ Assert.Null(batchSummaryFromStart.ExecutionElapsed);
+ Assert.Null(batchSummaryFromStart.ExecutionEnd);
+
// ... The callback for batch completion should have been fired
- Assert.NotNull(batchSummaryFromCallback);
+ // ... The summary should match the expected info
+ Assert.NotNull(batchSummaryFromCompletion);
+ Assert.False(batchSummaryFromCompletion.HasError);
+ Assert.Equal(Common.Ordinal, batchSummaryFromCompletion.Id);
+ Assert.Equal(0, batchSummaryFromCompletion.ResultSetSummaries.Length);
+ Assert.Equal(1, batchSummaryFromCompletion.Messages.Length);
+ Assert.Equal(Common.SubsectionDocument, batchSummaryFromCompletion.Selection);
+ Assert.True(DateTime.Parse(batchSummaryFromCompletion.ExecutionStart) > default(DateTime));
+ Assert.True(DateTime.Parse(batchSummaryFromCompletion.ExecutionEnd) > default(DateTime));
+ Assert.NotNull(batchSummaryFromCompletion.ExecutionElapsed);
// ... The callback for the result set should NOT have been fired
Assert.False(resultCallbackFired);
-
- // ... The summary should have the same info
- Assert.False(batch.Summary.HasError);
- Assert.Equal(Common.Ordinal, batch.Summary.Id);
- Assert.Equal(0, batch.Summary.ResultSetSummaries.Length);
- Assert.Equal(1, batch.Summary.Messages.Length);
- Assert.Equal(0, batch.Summary.Selection.StartLine);
- Assert.True(DateTime.Parse(batch.Summary.ExecutionStart) > default(DateTime));
- Assert.True(DateTime.Parse(batch.Summary.ExecutionEnd) > default(DateTime));
- Assert.NotNull(batch.Summary.ExecutionElapsed);
}
[Fact]
@@ -239,9 +263,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
[Fact]
public void BatchExecuteInvalidQuery()
{
- // Setup: Create a callback for batch completion
+ // Setup:
+ // ... Create a callback for batch start
+ bool batchStartCalled = false;
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
+ {
+ batchStartCalled = true;
+ return Task.FromResult(0);
+ };
+
+ // ... Create a callback for batch completion
BatchSummary batchSummaryFromCallback = null;
- Batch.BatchAsyncEventHandler batchCallback = b =>
+ Batch.BatchAsyncEventHandler batchCompleteCallback = b =>
{
batchSummaryFromCallback = b.Summary;
return Task.FromResult(0);
@@ -258,7 +291,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// If I execute a batch that is invalid
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Batch batch = new Batch(Common.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory);
- batch.BatchCompletion += batchCallback;
+ batch.BatchStart += batchStartCallback;
+ batch.BatchCompletion += batchCompleteCallback;
batch.ResultSetCompletion += resultSetCallback;
batch.Execute(GetConnection(ci), CancellationToken.None).Wait();
@@ -276,6 +310,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... The callback for batch completion should have been fired
Assert.NotNull(batchSummaryFromCallback);
+
+ // ... The callback for batch start should have been fired
+ Assert.True(batchStartCalled);
}
[Fact]
@@ -293,20 +330,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.True(batch.HasExecuted, "The batch should have been marked executed.");
Assert.False(batch.HasError, "The batch should not have an error");
- // Setup for part 2: Create a callback for batch completion
- BatchSummary batchSummaryFromCallback = null;
- bool completionCallbackFired = false;
- Batch.BatchAsyncEventHandler callback = b =>
+ // Setup for part 2:
+ // ... Create a callback for batch completion
+ Batch.BatchAsyncEventHandler completeCallback = b =>
{
- completionCallbackFired = true;
- batchSummaryFromCallback = b.Summary;
- return Task.FromResult(0);
+ throw new Exception("Batch completion callback should not have been called");
+ };
+
+ // ... Create a callback for batch start
+ Batch.BatchAsyncEventHandler startCallback = b =>
+ {
+ throw new Exception("Batch start callback should not have been called");
};
// If I execute it again
// Then:
// ... It should throw an invalid operation exception
- batch.BatchCompletion += callback;
+ batch.BatchStart += startCallback;
+ batch.BatchCompletion += completeCallback;
await Assert.ThrowsAsync(() =>
batch.Execute(GetConnection(ci), CancellationToken.None));
@@ -315,10 +356,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.True(batch.HasExecuted, "The batch should still be marked executed.");
Assert.NotEmpty(batch.ResultSets);
Assert.NotEmpty(batch.ResultSummaries);
-
- // ... The callback for batch completion should not have been fired for the second run
- Assert.False(completionCallbackFired);
- Assert.Null(batchSummaryFromCallback);
}
[Theory]
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/QueryTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/QueryTests.cs
index c3ff025b..3df09e13 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/QueryTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/QueryTests.cs
@@ -63,11 +63,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public void QueryExecuteSingleBatch()
{
// Setup:
- // ... Create a callback for batch completion
- int batchCallbacksReceived = 0;
- Batch.BatchAsyncEventHandler batchCallback = summary =>
+ // ... Create a callback for atch start
+ int batchStartCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
{
- batchCallbacksReceived++;
+ batchStartCallbacksReceived++;
+ return Task.FromResult(0);
+ };
+
+ // ... Create a callback for batch completion
+ int batchCompleteCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchCompleteCallback = summary =>
+ {
+ batchCompleteCallbacksReceived++;
return Task.CompletedTask;
};
@@ -76,7 +84,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Query query = new Query(Common.StandardQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
- query.BatchCompleted += batchCallback;
+ query.BatchStarted += batchStartCallback;
+ query.BatchCompleted += batchCompleteCallback;
// Then:
// ... I should get a single batch to execute that hasn't been executed
@@ -97,16 +106,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
- // ... The batch callback should have been called precisely 1 time
- Assert.Equal(1, batchCallbacksReceived);
+ // ... The batch callbacks should have been called precisely 1 time
+ Assert.Equal(1, batchStartCallbacksReceived);
+ Assert.Equal(1, batchCompleteCallbacksReceived);
}
[Fact]
public void QueryExecuteNoOpBatch()
{
// Setup:
+ // ... Create a callback for batch startup
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
+ {
+ throw new Exception("Batch startup callback should not have been called.");
+ };
+
// ... Create a callback for batch completion
- Batch.BatchAsyncEventHandler batchCallback = summary =>
+ Batch.BatchAsyncEventHandler batchCompletionCallback = summary =>
{
throw new Exception("Batch completion callback was called");
};
@@ -116,7 +132,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Query query = new Query(Common.NoOpQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
- query.BatchCompleted += batchCallback;
+ query.BatchStarted += batchStartCallback;
+ query.BatchCompleted += batchCompletionCallback;
// Then:
// ... I should get no batches back
@@ -140,12 +157,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
public void QueryExecuteMultipleBatches()
{
// Setup:
- // ... Create a callback for batch completion
- int batchCallbacksReceived = 0;
- Batch.BatchAsyncEventHandler batchCallback = summary =>
+ // ... Create a callback for batch start
+ int batchStartCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
{
- batchCallbacksReceived++;
- return Task.CompletedTask;
+ batchStartCallbacksReceived++;
+ return Task.FromResult(0);
+ };
+
+ // ... Create a callback for batch completion
+ int batchCompletedCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchCompletedCallback = summary =>
+ {
+ batchCompletedCallbacksReceived++;
+ return Task.FromResult(0);
};
// If:
@@ -154,7 +179,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
string queryText = string.Format("{0}\r\nGO\r\n{0}", Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Query query = new Query(queryText, ci, new QueryExecutionSettings(), fileStreamFactory);
- query.BatchCompleted += batchCallback;
+ query.BatchStarted += batchStartCallback;
+ query.BatchCompleted += batchCompletedCallback;
// Then:
// ... I should get back two batches to execute that haven't been executed
@@ -175,19 +201,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(2, query.BatchSummaries.Length);
- // ... The batch callback should have been called precisely 2 times
- Assert.Equal(2, batchCallbacksReceived);
+ // ... The batch start and completion callbacks should have been called precisely 2 times
+ Assert.Equal(2, batchStartCallbacksReceived);
+ Assert.Equal(2, batchCompletedCallbacksReceived);
}
[Fact]
public void QueryExecuteMultipleBatchesWithNoOp()
{
// Setup:
- // ... Create a callback for batch completion
- int batchCallbacksReceived = 0;
- Batch.BatchAsyncEventHandler batchCallback = summary =>
+ // ... Create a callback for batch start
+ int batchStartCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
{
- batchCallbacksReceived++;
+ batchStartCallbacksReceived++;
+ return Task.FromResult(0);
+ };
+
+ // ... Create a callback for batch completion
+ int batchCompletionCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchCompletionCallback = summary =>
+ {
+ batchCompletionCallbacksReceived++;
return Task.CompletedTask;
};
@@ -197,7 +232,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
string queryText = string.Format("{0}\r\nGO\r\n{1}", Common.StandardQuery, Common.NoOpQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Query query = new Query(queryText, ci, new QueryExecutionSettings(), fileStreamFactory);
- query.BatchCompleted += batchCallback;
+ query.BatchStarted += batchStartCallback;
+ query.BatchCompleted += batchCompletionCallback;
// Then:
// ... I should get back one batch to execute that hasn't been executed
@@ -217,19 +253,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
- // ... The batch callback should have been called precisely 1 time
- Assert.Equal(1, batchCallbacksReceived);
+ // ... The batch callbacks should have been called precisely 1 time
+ Assert.Equal(1, batchStartCallbacksReceived);
+ Assert.Equal(1, batchCompletionCallbacksReceived);
}
[Fact]
public void QueryExecuteInvalidBatch()
{
// Setup:
- // ... Create a callback for batch completion
- int batchCallbacksReceived = 0;
- Batch.BatchAsyncEventHandler batchCallback = summary =>
+ // ... Create a callback for batch start
+ int batchStartCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchStartCallback = b =>
{
- batchCallbacksReceived++;
+ batchStartCallbacksReceived++;
+ return Task.FromResult(0);
+ };
+
+ // ... Create a callback for batch completion
+ int batchCompletionCallbacksReceived = 0;
+ Batch.BatchAsyncEventHandler batchCompltionCallback = summary =>
+ {
+ batchCompletionCallbacksReceived++;
return Task.CompletedTask;
};
@@ -238,7 +283,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary());
Query query = new Query(Common.InvalidQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
- query.BatchCompleted += batchCallback;
+ query.BatchStarted += batchStartCallback;
+ query.BatchCompleted += batchCompltionCallback;
// Then:
// ... I should get back a query with one batch not executed
@@ -261,8 +307,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.True(query.BatchSummaries[0].HasError);
Assert.NotEmpty(query.BatchSummaries[0].Messages);
- // ... The batch callback should have been called once
- Assert.Equal(1, batchCallbacksReceived);
+ // ... The batch callbacks should have been called once
+ Assert.Equal(1, batchStartCallbacksReceived);
+ Assert.Equal(1, batchCompletionCallbacksReceived);
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/ServiceIntegrationTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/ServiceIntegrationTests.cs
index fa2151c9..c62211b0 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/ServiceIntegrationTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/ServiceIntegrationTests.cs
@@ -36,9 +36,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
- QueryExecuteBatchCompleteParams batchCompleteParams = null;
+ QueryExecuteBatchNotificationParams batchStartParams = null;
+ QueryExecuteBatchNotificationParams batchCompleteParams = null;
var requestContext = RequestContextMocks.Create(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
+ .AddEventHandling(QueryExecuteBatchStartEvent.Type, (et, p) => batchStartParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p)
.AddEventHandling(QueryExecuteResultSetCompleteEvent.Type, null);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
@@ -49,14 +51,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... A completion event should have been fired with empty results
// ... A batch completion event should have been fired with empty results
// ... A result set completion event should not have been fired
- VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never(), Times.Never());
+ VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Never(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
Assert.Empty(completeParams.BatchSummaries[0].ResultSetSummaries);
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
+ // ... Batch start summary should not contain result sets, messages, but should contain owner URI
+ Assert.NotNull(batchStartParams);
+ Assert.NotNull(batchStartParams.BatchSummary);
+ Assert.Null(batchStartParams.BatchSummary.Messages);
+ Assert.Null(batchStartParams.BatchSummary.ResultSetSummaries);
+ Assert.Equal(Common.OwnerUri, batchStartParams.OwnerUri);
+
+ // ... Batch completion summary should contain result sets, messages, and the owner URI
Assert.NotNull(batchCompleteParams);
+ Assert.NotNull(batchCompleteParams.BatchSummary);
Assert.Empty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
Assert.Equal(Common.OwnerUri, batchCompleteParams.OwnerUri);
@@ -80,10 +91,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
- QueryExecuteBatchCompleteParams batchCompleteParams = null;
+ QueryExecuteBatchNotificationParams batchStartParams = null;
+ QueryExecuteBatchNotificationParams batchCompleteParams = null;
QueryExecuteResultSetCompleteParams resultCompleteParams = null;
var requestContext = RequestContextMocks.Create(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
+ .AddEventHandling(QueryExecuteBatchStartEvent.Type, (et, p) => batchStartParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p)
.AddEventHandling(QueryExecuteResultSetCompleteEvent.Type, (et, p) => resultCompleteParams = p);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
@@ -94,7 +107,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... A completion event should have been fired with one result
// ... A batch completion event should have been fired
// ... A resultset completion event should have been fired
- VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Never());
+ VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
@@ -102,6 +115,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.False(completeParams.BatchSummaries[0].HasError);
+ // ... Batch start summary should not contain result sets, messages, but should contain owner URI
+ Assert.NotNull(batchStartParams);
+ Assert.NotNull(batchStartParams.BatchSummary);
+ Assert.Null(batchStartParams.BatchSummary.Messages);
+ Assert.Null(batchStartParams.BatchSummary.ResultSetSummaries);
+ Assert.Equal(Common.OwnerUri, batchStartParams.OwnerUri);
+
Assert.NotNull(batchCompleteParams);
Assert.NotEmpty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
@@ -131,10 +151,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
- QueryExecuteBatchCompleteParams batchCompleteParams = null;
+ QueryExecuteBatchNotificationParams batchStartParams = null;
+ QueryExecuteBatchNotificationParams batchCompleteParams = null;
List resultCompleteParams = new List();
var requestContext = RequestContextMocks.Create(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
+ .AddEventHandling(QueryExecuteBatchStartEvent.Type, (et, p) => batchStartParams = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams = p)
.AddEventHandling(QueryExecuteResultSetCompleteEvent.Type, (et, p) => resultCompleteParams.Add(p));
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
@@ -145,7 +167,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... A completion event should have been fired with one result
// ... A batch completion event should have been fired
// ... Two resultset completion events should have been fired
- VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Exactly(2), Times.Never());
+ VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Exactly(2), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, completeParams.BatchSummaries.Length);
@@ -153,6 +175,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.False(completeParams.BatchSummaries[0].HasError);
+ // ... Batch start summary should not contain result sets, messages, but should contain owner URI
+ Assert.NotNull(batchStartParams);
+ Assert.NotNull(batchStartParams.BatchSummary);
+ Assert.Null(batchStartParams.BatchSummary.Messages);
+ Assert.Null(batchStartParams.BatchSummary.ResultSetSummaries);
+ Assert.Equal(Common.OwnerUri, batchStartParams.OwnerUri);
+
Assert.NotNull(batchCompleteParams);
Assert.NotEmpty(batchCompleteParams.BatchSummary.ResultSetSummaries);
Assert.NotEmpty(batchCompleteParams.BatchSummary.Messages);
@@ -183,10 +212,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
QueryExecuteResult result = null;
QueryExecuteCompleteParams completeParams = null;
- List batchCompleteParams = new List();
+ List batchStartParams = new List();
+ List batchCompleteParams = new List();
List resultCompleteParams = new List();
var requestContext = RequestContextMocks.Create(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, p) => completeParams = p)
+ .AddEventHandling(QueryExecuteBatchStartEvent.Type, (et, p) => batchStartParams.Add(p))
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchCompleteParams.Add(p))
.AddEventHandling(QueryExecuteResultSetCompleteEvent.Type, (et, p) => resultCompleteParams.Add(p));
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
@@ -195,7 +226,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... No errors should have been sent
// ... A successful result should have been sent without messages
- VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Exactly(2), Times.Exactly(2), Times.Never());
+ VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Exactly(2), Times.Exactly(2), Times.Exactly(2), Times.Never());
Assert.Null(result.Messages);
// ... A completion event should have been fired with one two batch summaries, one result each
@@ -205,6 +236,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
Assert.NotEmpty(completeParams.BatchSummaries[0].Messages);
Assert.NotEmpty(completeParams.BatchSummaries[1].Messages);
+ // ... Two batch start events should have been fired
+ Assert.Equal(2, batchStartParams.Count);
+ foreach (var batch in batchStartParams)
+ {
+ Assert.Null(batch.BatchSummary.Messages);
+ Assert.Null(batch.BatchSummary.ResultSetSummaries);
+ Assert.Equal(Common.OwnerUri, batch.OwnerUri);
+ }
+
// ... Two batch completion events should have been fired
Assert.Equal(2, batchCompleteParams.Count);
foreach (var batch in batchCompleteParams)
@@ -250,7 +290,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... No result should have been returned
// ... No completion event should have been fired
// ... There should be no active queries
- VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Never(), Times.Once());
+ VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Never(), Times.Never(), Times.Once());
Assert.IsType(error);
Assert.NotEmpty((string)error);
Assert.Empty(queryService.ActiveQueries);
@@ -285,7 +325,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... No completion event should have been fired
// ... A batch completion event should have fired, but not a resultset event
// ... There should only be one active query
- VerifyQueryExecuteCallCount(secondRequestContext, Times.Never(), Times.AtMostOnce(), Times.AtMostOnce(), Times.Never(), Times.Once());
+ VerifyQueryExecuteCallCount(secondRequestContext, Times.Never(), Times.AtMostOnce(), Times.AtMostOnce(), Times.AtMostOnce(), Times.Never(), Times.Once());
Assert.IsType(error);
Assert.NotEmpty((string)error);
Assert.Equal(1, queryService.ActiveQueries.Count);
@@ -311,9 +351,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... And then I request another query after waiting for the first to complete
QueryExecuteResult result = null;
QueryExecuteCompleteParams complete = null;
- QueryExecuteBatchCompleteParams batchComplete = null;
+ QueryExecuteBatchNotificationParams batchStart = null;
+ QueryExecuteBatchNotificationParams batchComplete = null;
var secondRequestContext = RequestContextMocks.Create(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, qecp) => complete = qecp)
+ .AddEventHandling(QueryExecuteBatchStartEvent.Type, (et, p) => batchStart = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchComplete = p);
await Common.AwaitExecution(queryService, queryParams, secondRequestContext.Object);
@@ -322,20 +364,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... A result should have been sent with no errors
// ... There should only be one active query
// ... A batch completion event should have fired, but not a result set completion event
- VerifyQueryExecuteCallCount(secondRequestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never(), Times.Never());
+ VerifyQueryExecuteCallCount(secondRequestContext, Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Never(), Times.Never());
Assert.Null(result.Messages);
Assert.False(complete.BatchSummaries.Any(b => b.HasError));
Assert.Equal(1, queryService.ActiveQueries.Count);
+ Assert.NotNull(batchStart);
Assert.NotNull(batchComplete);
Assert.False(batchComplete.BatchSummary.HasError);
Assert.Equal(complete.OwnerUri, batchComplete.OwnerUri);
}
- [Theory]
- [InlineData(null)]
- public async Task QueryExecuteMissingSelectionTest(SelectionData selection)
+ [Fact]
+ public async Task QueryExecuteMissingSelectionTest()
{
// Given:
// ... A workspace with a standard query is configured
@@ -357,7 +399,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... No result should have been sent
// ... No completion events should have been fired
// ... An active query should not have been added
- VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Never(), Times.Once());
+ VerifyQueryExecuteCallCount(requestContext, Times.Never(), Times.Never(), Times.Never(), Times.Never(), Times.Never(), Times.Once());
Assert.NotNull(errorResult);
Assert.IsType(errorResult);
Assert.DoesNotContain(Common.OwnerUri, queryService.ActiveQueries.Keys);
@@ -380,9 +422,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
QueryExecuteResult result = null;
QueryExecuteCompleteParams complete = null;
- QueryExecuteBatchCompleteParams batchComplete = null;
+ QueryExecuteBatchNotificationParams batchStart = null;
+ QueryExecuteBatchNotificationParams batchComplete = null;
var requestContext = RequestContextMocks.Create(qer => result = qer)
.AddEventHandling(QueryExecuteCompleteEvent.Type, (et, qecp) => complete = qecp)
+ .AddEventHandling(QueryExecuteBatchStartEvent.Type, (et, p) => batchStart = p)
.AddEventHandling(QueryExecuteBatchCompleteEvent.Type, (et, p) => batchComplete = p);
await Common.AwaitExecution(queryService, queryParams, requestContext.Object);
@@ -390,29 +434,43 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
// ... No errors should have been sent
// ... A result should have been sent with success (we successfully started the query)
// ... A completion event (query, batch, not resultset) should have been sent with error
- VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Never(), Times.Never());
+ VerifyQueryExecuteCallCount(requestContext, Times.Once(), Times.Once(), Times.Once(), Times.Once(), Times.Never(), Times.Never());
Assert.Null(result.Messages);
Assert.Equal(1, complete.BatchSummaries.Length);
Assert.True(complete.BatchSummaries[0].HasError);
Assert.NotEmpty(complete.BatchSummaries[0].Messages);
+ Assert.NotNull(batchStart);
+ Assert.False(batchStart.BatchSummary.HasError);
+ Assert.Null(batchStart.BatchSummary.Messages);
+ Assert.Null(batchStart.BatchSummary.ResultSetSummaries);
+ Assert.Equal(Common.OwnerUri, batchStart.OwnerUri);
+
Assert.NotNull(batchComplete);
Assert.True(batchComplete.BatchSummary.HasError);
Assert.NotEmpty(batchComplete.BatchSummary.Messages);
- Assert.Equal(complete.OwnerUri, batchComplete.OwnerUri);
+ Assert.Equal(Common.OwnerUri, batchComplete.OwnerUri);
}
- private static void VerifyQueryExecuteCallCount(Mock> mock, Times sendResultCalls,
- Times sendCompletionEventCalls, Times sendBatchCompletionEvent, Times sendResultCompleteEvent, Times sendErrorCalls)
+ private static void VerifyQueryExecuteCallCount(Mock> mock,
+ Times sendResultCalls,
+ Times sendCompletionEventCalls,
+ Times sendBatchStartEvent,
+ Times sendBatchCompletionEvent,
+ Times sendResultCompleteEvent,
+ Times sendErrorCalls)
{
mock.Verify(rc => rc.SendResult(It.IsAny()), sendResultCalls);
mock.Verify(rc => rc.SendEvent(
It.Is>(m => m == QueryExecuteCompleteEvent.Type),
It.IsAny()), sendCompletionEventCalls);
mock.Verify(rc => rc.SendEvent(
- It.Is>(m => m == QueryExecuteBatchCompleteEvent.Type),
- It.IsAny()), sendBatchCompletionEvent);
+ It.Is>(m => m == QueryExecuteBatchCompleteEvent.Type),
+ It.IsAny()), sendBatchCompletionEvent);
+ mock.Verify(rc => rc.SendEvent(
+ It.Is>(m => m== QueryExecuteBatchStartEvent.Type),
+ It.IsAny()), sendBatchStartEvent);
mock.Verify(rc => rc.SendEvent(
It.Is>(m => m == QueryExecuteResultSetCompleteEvent.Type),
It.IsAny()), sendResultCompleteEvent);