Support GO N syntax to execute multiple times (#551)

* Support `GO N` syntax to execute multiple times
- Plumbed through the batch execution count from the parser and used in the batch execution code path
- Functionality matches SSMS:
  - Outputs loop start/end messages that match SSMS if you're doing multi-batch execution
  - Outputs an "ignoring failure" error if an error happens during a batch
- Added tests for this
- Manually verified end to end also

* Fixing test error
This commit is contained in:
Kevin Cunnane
2017-11-22 11:33:19 -08:00
committed by GitHub
parent 42ee96f99f
commit b8e46ce65f
35 changed files with 623 additions and 367 deletions

View File

@@ -1,61 +1,61 @@
// WARNING:
// This file was generated by the Microsoft DataWarehouse String Resource Tool 1.37.0.0
// from information in sr.strings
// DO NOT MODIFY THIS FILE'S CONTENTS, THEY WILL BE OVERWRITTEN
//
namespace Microsoft.SqlTools.ResourceProvider
{
using System;
using System.Reflection;
using System.Resources;
using System.Globalization;
// WARNING:
// This file was generated by the Microsoft DataWarehouse String Resource Tool 1.37.0.0
// from information in sr.strings
// DO NOT MODIFY THIS FILE'S CONTENTS, THEY WILL BE OVERWRITTEN
//
namespace Microsoft.SqlTools.ResourceProvider
{
using System;
using System.Reflection;
using System.Resources;
using System.Globalization;
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class SR
{
protected SR()
{ }
public static CultureInfo Culture
{
get
{
return Keys.Culture;
}
set
{
Keys.Culture = value;
}
}
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class SR
{
protected SR()
{ }
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
{
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ResourceProvider.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
static CultureInfo _culture = null;
public static CultureInfo Culture
{
get
{
return Keys.Culture;
}
set
{
Keys.Culture = value;
}
}
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
{
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ResourceProvider.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
static CultureInfo _culture = null;
private Keys()
{ }
public static CultureInfo Culture
{
get
{
return _culture;
}
set
{
_culture = value;
}
}
public static string GetString(string key)
{
return resourceManager.GetString(key, _culture);
}
private Keys()
{ }
public static CultureInfo Culture
{
get
{
return _culture;
}
set
{
_culture = value;
}
}
public static string GetString(string key)
{
return resourceManager.GetString(key, _culture);
}
}
}

View File

@@ -1,120 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype=">text/microsoft-resx</resheader>
<resheader name="version=">2.0</resheader>
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer=">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1="><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata=">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true=">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded=">
<xsd:element name="metadata=">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly=">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data=">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader=">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype=">text/microsoft-resx</resheader>
<resheader name="version=">2.0</resheader>
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer=">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1="><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata=">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true=">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded=">
<xsd:element name="metadata=">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly=">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data=">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader=">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -17,52 +17,49 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
/// </summary>
public sealed class BatchParserWrapper : IDisposable
{
private List<Tuple<int /*startLine*/, int/*startColumn*/>> startLineColumns;
private List<int /*length*/> lengths;
private List<BatchInfo> batchInfos;
private ExecutionEngine executionEngine;
private BatchEventNotificationHandler notificationHandler;
/// <summary>
/// Helper method used to Convert line/column information in a file to offset
/// </summary>
private static List<BatchDefinition> ConvertToBatchDefinitionList(string content,
IList<Tuple<int, int>> positions, List<int> lengths)
private static List<BatchDefinition> ConvertToBatchDefinitionList(string content, List<BatchInfo> batchInfos)
{
List<BatchDefinition> batchDefinitionList = new List<BatchDefinition>();
if (positions.Count == 0 && lengths.Count == 0)
if (batchInfos.Count == 0)
{
return batchDefinitionList;
}
List<int> offsets = GetOffsets(content, positions);
List<int> offsets = GetOffsets(content, batchInfos);
if (!string.IsNullOrEmpty(content) && (positions.Count > 0))
if (!string.IsNullOrEmpty(content) && (batchInfos.Count > 0))
{
// Instantiate a string reader for the whole sql content
using (StringReader reader = new StringReader(content))
{
// Generate the first batch definition list
int startLine = positions[0].Item1 + 1; //positions is 0 index based
int startLine = batchInfos[0].startLine + 1; //positions is 0 index based
int endLine = startLine;
int lineDifference = startLine - 1;
int endColumn;
int offset = offsets[0];
int startColumn = positions[0].Item2;
int count = positions.Count;
string batchText = content.Substring(offset, lengths[0]);
int startColumn = batchInfos[0].startColumn;
int count = batchInfos.Count;
string batchText = content.Substring(offset, batchInfos[0].length);
// if there's only one batch then the line difference is just startLine
if (count > 1)
{
lineDifference = positions[1].Item1 - positions[0].Item1;
lineDifference = batchInfos[1].startLine - batchInfos[0].startLine;
}
// get endLine, endColumn for the current batch and the lineStartOffset for the next batch
var batchInfo = ReadLines(reader, lineDifference, endLine);
endLine = batchInfo.Item1;
endColumn = batchInfo.Item2;
var position = ReadLines(reader, lineDifference, endLine);
endLine = position.Item1;
endColumn = position.Item2;
// create a new BatchDefinition and add it to the list
BatchDefinition batchDef = new BatchDefinition(
@@ -70,27 +67,28 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
startLine,
endLine,
startColumn + 1,
endColumn + 1
endColumn + 1,
batchInfos[0].executionCount
);
batchDefinitionList.Add(batchDef);
if (count > 1)
{
offset = offsets[1] + positions[0].Item2;
offset = offsets[1] + batchInfos[0].startColumn;
}
// Generate the rest batch definitions
for (int index = 1; index < count - 1; index++)
{
lineDifference = positions[index + 1].Item1 - positions[index].Item1;
batchInfo = ReadLines(reader, lineDifference, endLine);
endLine = batchInfo.Item1;
endColumn = batchInfo.Item2;
lineDifference = batchInfos[index + 1].startLine - batchInfos[index].startLine;
position = ReadLines(reader, lineDifference, endLine);
endLine = position.Item1;
endColumn = position.Item2;
offset = offsets[index];
batchText = content.Substring(offset, lengths[index]);
startLine = positions[index].Item1;
startColumn = positions[index].Item2;
batchText = content.Substring(offset, batchInfos[index].length);
startLine = batchInfos[index].startLine;
startColumn = batchInfos[index].startColumn;
// make a new batch definition for each batch
BatchDefinition batch = new BatchDefinition(
@@ -98,7 +96,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
startLine,
endLine,
startColumn + 1,
endColumn + 1
endColumn + 1,
batchInfos[index].executionCount
);
batchDefinitionList.Add(batch);
}
@@ -107,8 +106,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
if (count > 1)
{
batchText = content.Substring(offsets[count-1], lengths[count - 1]);
BatchDefinition lastBatchDef = GetLastBatchDefinition(reader, positions[count - 1], batchText);
batchText = content.Substring(offsets[count-1], batchInfos[count - 1].length);
BatchDefinition lastBatchDef = GetLastBatchDefinition(reader, batchInfos[count - 1], batchText);
batchDefinitionList.Add(lastBatchDef);
}
@@ -117,14 +116,14 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
return batchDefinitionList;
}
private static int GetMaxStartLine(IList<Tuple<int, int>> positions)
private static int GetMaxStartLine(IList<BatchInfo> batchInfos)
{
int highest = 0;
foreach (var position in positions)
foreach (var batchInfo in batchInfos)
{
if (position.Item1 > highest)
if (batchInfo.startLine > highest)
{
highest = position.Item1;
highest = batchInfo.startLine;
}
}
return highest;
@@ -133,14 +132,14 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
/// <summary>
/// Gets offsets for all batches
/// </summary>
private static List<int> GetOffsets(string content, IList<Tuple<int, int>> positions)
private static List<int> GetOffsets(string content, IList<BatchInfo> batchInfos)
{
List<int> offsets = new List<int>();
int count = 0;
int offset = 0;
bool foundAllOffsets = false;
int maxStartLine = GetMaxStartLine(positions);
int maxStartLine = GetMaxStartLine(batchInfos);
using (StringReader reader = new StringReader(content))
{
// go until we have found offsets for all batches
@@ -150,7 +149,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
for (int i = 0; i <= maxStartLine ; i++)
{
// get offset for the current batch
ReadLines(reader, ref count, ref offset, ref foundAllOffsets, positions, offsets, i);
ReadLines(reader, ref count, ref offset, ref foundAllOffsets, batchInfos, offsets, i);
// if we found all the offsets, then we're done
if (foundAllOffsets)
@@ -168,16 +167,16 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
/// Helper function to read lines of batches to get offsets
/// </summary>
private static void ReadLines(StringReader reader, ref int count, ref int offset, ref bool foundAllOffsets,
IList<Tuple<int, int>> positions, List<int> offsets, int iteration)
IList<BatchInfo> batchInfos, List<int> offsets, int iteration)
{
int ch;
while (true)
{
if (positions[count].Item1 == iteration)
if (batchInfos[count].startLine == iteration)
{
count++;
offsets.Add(offset);
if (count == positions.Count)
if (count == batchInfos.Count)
{
foundAllOffsets = true;
break;
@@ -205,10 +204,10 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
/// Helper method to get the last batch
/// </summary>
private static BatchDefinition GetLastBatchDefinition(StringReader reader,
Tuple<int, int> position, string batchText)
BatchInfo batchInfo, string batchText)
{
int startLine = position.Item1;
int startColumn = position.Item2;
int startLine = batchInfo.startLine;
int startColumn = batchInfo.startColumn;
string prevLine = null;
string line = reader.ReadLine();
int endLine = startLine;
@@ -232,7 +231,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
startLine,
endLine,
startColumn + 1,
endColumn + 1
endColumn + 1,
batchInfo.executionCount
);
}
@@ -240,7 +240,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
/// Helper function to get correct lines and columns
/// in a single batch with multiple statements
/// </summary>
private static Tuple<int, int> GetBatchDetails(StringReader reader, int endLine)
private static Tuple<int, int> GetBatchPositionDetails(StringReader reader, int endLine)
{
string prevLine = null;
string line = reader.ReadLine();
@@ -274,7 +274,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
// if only one batch with multiple lines
if (n == 0)
{
return GetBatchDetails(reader, endLine);
return GetBatchPositionDetails(reader, endLine);
}
// if there are more than one batch
@@ -327,15 +327,13 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
/// </summary>
public List<BatchDefinition> GetBatches(string sqlScript)
{
startLineColumns = new List<System.Tuple<int /*startLine*/, int /*startColumn*/>>();
lengths = new List<int /* length */>();
batchInfos = new List<BatchInfo>();
// execute the script - all communication / integration after here happen via event handlers
executionEngine.ParseScript(sqlScript, notificationHandler);
// retrieve a list of BatchDefinitions
List<BatchDefinition> batchDefinitionList = ConvertToBatchDefinitionList(sqlScript, startLineColumns,
lengths);
List<BatchDefinition> batchDefinitionList = ConvertToBatchDefinitionList(sqlScript, batchInfos);
return batchDefinitionList;
}
@@ -360,10 +358,6 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
{
if (args != null && args.Batch != null)
{
Tuple<int /*startLine*/, int/*startColumn*/> position = new Tuple<int, int>(args.Batch.TextSpan.iStartLine, args.Batch.TextSpan.iStartIndex);
// PS168371
//
// There is a bug in the batch parser where it appends a '\n' to the end of the last
@@ -383,8 +377,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
}
// Add the script info
startLineColumns.Add(position);
lengths.Add(batchTextLength);
batchInfos.Add(new BatchInfo(args.Batch.TextSpan.iStartLine, args.Batch.TextSpan.iStartIndex, batchTextLength, args.Batch.ExpectedExecutionCount));
}
}
catch (NotImplementedException)
@@ -469,11 +462,26 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
{
executionEngine.Dispose();
executionEngine = null;
startLineColumns = null;
lengths = null;
batchInfos = null;
}
}
}
#endregion
private class BatchInfo
{
public BatchInfo(int startLine, int startColumn, int length, int repeatCount = 1)
{
this.startLine = startLine;
this.startColumn = startColumn;
this.length = length;
this.executionCount = repeatCount;
}
public int startLine;
public int startColumn;
public int length;
public int executionCount;
}
}
}

View File

@@ -214,6 +214,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
//index of the batch in collection of batches
private int index = 0;
private int expectedExecutionCount = 1;
private long totalAffectedRows = 0;
private bool hasErrors;
@@ -343,6 +345,22 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
}
}
/// <summary>
/// The number of times this batch is expected to be executed. Will be 1 by default, but for statements
/// with "GO 2" or other numerical values, will have a number > 1
/// </summary>
public int ExpectedExecutionCount
{
get
{
return expectedExecutionCount;
}
set
{
expectedExecutionCount = value;
}
}
/// <summary>
/// Returns how many rows were affected. It should be the value that can be shown

View File

@@ -13,13 +13,15 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
/// <summary>
/// Constructor method for a BatchDefinition
/// </summary>
public BatchDefinition(string batchText, int startLine, int endLine, int startColumn, int endColumn)
public BatchDefinition(string batchText, int startLine, int endLine, int startColumn, int endColumn, int executionCount)
{
BatchText = batchText;
StartLine = startLine;
EndLine = endLine;
StartColumn = startColumn;
EndColumn = endColumn;
// set the batch execution count, with min value of 1
BatchExecutionCount = executionCount > 0 ? executionCount : 1;
}
/// <summary>
@@ -55,11 +57,19 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
}
/// <summary>
/// Get batch text assocaited with the BatchDefinition
/// Get batch text associated with the BatchDefinition
/// </summary>
public string BatchText
{
get; private set;
}
}
/// <summary>
/// Get number of times to execute this batch
/// </summary>
public int BatchExecutionCount
{
get; private set;
}
}
}

View File

@@ -72,7 +72,6 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
try
{
// Parsing mode we execute only once
if (conditions.IsParseOnly)
{
numBatchExecutionTimes = 1;
@@ -81,7 +80,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
int timesLoop = numBatchExecutionTimes;
if (numBatchExecutionTimes > 1)
{
RaiseBatchMessage(String.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_InitilizingLoop, numBatchExecutionTimes));
RaiseBatchMessage(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_InitializingLoop));
}
while (timesLoop > 0 && result != ScriptExecutionResult.Cancel && result != ScriptExecutionResult.Halted)
@@ -116,13 +115,13 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
if (result == ScriptExecutionResult.Cancel)
{
RaiseBatchMessage(String.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_QueryCancelledbyUser));
RaiseBatchMessage(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_QueryCancelledbyUser));
}
else
{
if (numBatchExecutionTimes > 1)
{
RaiseBatchMessage(String.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_FinalizingLoop, numBatchExecutionTimes));
RaiseBatchMessage(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_FinalizingLoop, numBatchExecutionTimes));
}
}
}
@@ -391,6 +390,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
currentBatch.Text = batchScript;
currentBatch.TextSpan = textSpan;
currentBatch.BatchIndex = currentBatchIndex;
currentBatch.ExpectedExecutionCount = numBatchExecutionTimes;
currentBatchIndex++;
@@ -489,7 +489,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
result = ScriptExecutionResult.Failure;
string info = ex.Text;
RaiseScriptError(String.Format(CultureInfo.CurrentCulture, SR.EE_ScriptError_ParsingSyntax, info), ScriptMessageType.FatalError);
RaiseScriptError(string.Format(CultureInfo.CurrentCulture, SR.EE_ScriptError_ParsingSyntax, info), ScriptMessageType.FatalError);
}
}
catch (Exception ex)
@@ -730,13 +730,13 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
// SET SHOWPLAN_TEXT cannot be used with other statements in the batch
preConditionBatches.Insert(0,
new Batch(
String.Format(CultureInfo.CurrentCulture, "{0} ", ExecutionEngineConditions.ShowPlanTextStatement(true)),
string.Format(CultureInfo.CurrentCulture, "{0} ", ExecutionEngineConditions.ShowPlanTextStatement(true)),
false,
executionTimeout));
postConditionBatches.Insert(0,
new Batch(
String.Format(CultureInfo.CurrentCulture, "{0} ", ExecutionEngineConditions.ShowPlanTextStatement(false)),
string.Format(CultureInfo.CurrentCulture, "{0} ", ExecutionEngineConditions.ShowPlanTextStatement(false)),
false,
executionTimeout));
}
@@ -938,7 +938,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
if (isSqlCmdConnection)
{
RaiseBatchMessage(String.Format(CultureInfo.CurrentCulture, "Disconnection from server {0}", ReliableConnectionHelper.GetServerName(connection)));
RaiseBatchMessage(string.Format(CultureInfo.CurrentCulture, "Disconnection from server {0}", ReliableConnectionHelper.GetServerName(connection)));
CloseConnection(connection);
}
}

View File

@@ -749,11 +749,11 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
public static string EE_ExecutionInfo_InitilizingLoop
public static string EE_ExecutionInfo_InitializingLoop
{
get
{
return Keys.GetString(Keys.EE_ExecutionInfo_InitilizingLoop);
return Keys.GetString(Keys.EE_ExecutionInfo_InitializingLoop);
}
}
@@ -3979,7 +3979,7 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string EE_BatchExecutionError_Ignoring = "EE_BatchExecutionError_Ignoring";
public const string EE_ExecutionInfo_InitilizingLoop = "EE_ExecutionInfo_InitilizingLoop";
public const string EE_ExecutionInfo_InitializingLoop = "EE_ExecutionInfo_InitializingLoop";
public const string EE_ExecutionError_CommandNotSupported = "EE_ExecutionError_CommandNotSupported";

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>Sie haben die Abfrage abgebrochen.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>Fehler während der Batchausführung.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>Fehler während der Batchausführung, aber des Fehlers wurde ignoriert.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>{0}-malige Batchausführung wurde gestartet.</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>{0}-malige Batchausführung wurde gestartet.</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>Befehl {0} wird nicht unterstützt.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>Die Variable {0} konnte nicht gefunden werden.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Fehler bei der SQL-Ausführung: {0}</value></data>
@@ -479,4 +479,12 @@ prototype_db_prop_parameterization = Parameterization</value></data>
<data name="BackupPathIsFolderError"><value>Der angegebene Dateiname ist zugleich ein Verzeichnisname: {0}</value></data>
<data name="InvalidBackupPathError"><value>Es kann nicht überprüft werden, ob der Speicherort der Sicherungsdatei vorhanden ist: {0}</value></data>
<data name="InvalidPathError"><value>Auf den angegebenen Pfad auf dem Server kann nicht zugegriffen werden: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>Kein Sicherungssatz zur Wiederherstellung ausgewählt</value></data>
<data name="NeverBackedUp"><value>Nie</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>Der Pfad {0} ist kein gültiges Verzeichnis</value></data>
<data name="Error_ExistingDirectoryName"><value>Die Datei {1} im Verzeichnis {0} existiert bereits.</value></data>
<data name="EditDataValueTooLarge"><value>Der Wert {0} ist zu groß für eine Spalte mit dem Datentyp {1}</value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>Se canceló la consulta.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>Se produjo un error mientras se ejecutaba el lote.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>Se produjo un error mientras se ejecutaba el lote, pero se ha omitido el error.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>Iniciando bucle de ejecución de {0} veces...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>Iniciando bucle de ejecución de {0} veces...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>No se admite el comando {0}.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>La variable {0} no se encontró.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Error de ejecución de SQL: {0}</value></data>
@@ -478,4 +478,12 @@
<data name="BackupPathIsFolderError"><value>El nombre del archivo especificado es un nombre de directorio: {0}</value></data>
<data name="InvalidBackupPathError"><value>No se puede verificar la existencia de la ubicación del archivo de copia de seguridad: {0}</value></data>
<data name="InvalidPathError"><value>No se puede acceder a la ruta de acceso especificada en el servidor: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>Ningún backupset seleccionado para ser restaurado</value></data>
<data name="NeverBackedUp"><value>Nunca</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL Database </value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>La ruta de acceso [{0}] no es un directorio válido</value></data>
<data name="Error_ExistingDirectoryName"><value>Ya existe un archivo {1} en el directorio '{0}'</value></data>
<data name="EditDataValueTooLarge"><value>El valor {0} es muy grande para el tipo de columna {1} </value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>Vous avez annulé la requête.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>Une erreur s'est produite lors de l'exécution du lot.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>Une erreur s'est produite lors de l'exécution du lot, mais elle a été ignorée.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>Démarrage de la boucle d'exécution pour {0} fois...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>Démarrage de la boucle d'exécution pour {0} fois...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>La commande {0} n'est pas prise en charge.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>Impossible de trouver la variable {0}.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Erreur dexécution de SQL : {0}</value></data>
@@ -478,4 +478,12 @@
<data name="BackupPathIsFolderError"><value>Le nom de fichier spécifié est également un nom de répertoire: {0}</value></data>
<data name="InvalidBackupPathError"><value>Impossible de vérifier l'existence de l'emplacement du fichier de sauvegarde: {0}</value></data>
<data name="InvalidPathError"><value>Impossible d'accéder au chemin d'accès spécifié sur le serveur: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>Aucun jeu de sauvegarde n'a été sélectionné pour être restauré.</value></data>
<data name="NeverBackedUp"><value>Jamais</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>Le chemin {0} n'est pas un répertoire valide.</value></data>
<data name="Error_ExistingDirectoryName"><value> Pour le répertoire {0} un fichier avec le nom {1} existe déjà</value></data>
<data name="EditDataValueTooLarge"><value>La valeur {0} est trop grande pour tenir dans la colonne de type {1}</value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>È stata annullata la query.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>Si è verificato un errore durante l'esecuzione del batch.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>Si è verificato un errore durante l'esecuzione del batch, ma l'errore è stato ignorato.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>Avvio ciclo di esecuzione di {0} volte...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>Avvio ciclo di esecuzione di {0} volte...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>Il comando {0} non è supportato.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>Impossibile trovare la variabile {0}.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Errore di esecuzione di SQL: {0}</value></data>
@@ -478,4 +478,12 @@
<data name="BackupPathIsFolderError"><value>Il nome file specificato è anche un nome di cartella: {0}</value></data>
<data name="InvalidBackupPathError"><value>Impossibile verificare l'esistenza della posizione del file di backup: {0}</value></data>
<data name="InvalidPathError"><value>Impossibile accedere al percorso specificato sul server: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>Nessun insieme di backup selezionato per il ripristino</value></data>
<data name="NeverBackedUp"><value>Mai</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>Il percorso [{0}] non è una directory valida.</value></data>
<data name="Error_ExistingDirectoryName"><value>Nella directory '{0}' esiste già il file {1}</value></data>
<data name="EditDataValueTooLarge"><value>Il valore {0} è troppo grande per essere contenuto in una colonna di tipo {1}</value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>クエリをキャンセルしました。</value></data>
<data name="EE_BatchExecutionError_Halting"><value>バッチの実行中にエラーが発生しました。</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>バッチの実行中にエラーが発生しましたが、エラーを無視しました。</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>{0} 回の実行ループを開始しています.</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>{0} 回の実行ループを開始しています.</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>コマンド {0} はサポートされていません。</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>変数 {0} が見つかりませんでした。</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>SQL の実行エラー: {0}</value></data>
@@ -451,8 +451,10 @@ Enabledprototype_db_prop_parameterization = Parameterization</value></data>
<data name="ConflictWithNoRecovery"><value>NORECOVERY オプションを使用してバックアップを復元するときにこのオプションを指定することはできません。</value></data>
<data name="InvalidPathForDatabaseFile"><value>データベース ファイルのパスが無効です: '{0}'</value></data>
<data name="Log"><value>ログ</value></data>
<data name="RestorePlanFailed"><value>リストア プランの作成に失敗しました。</value></data>
<data name="RestoreNotSupported"><value>データベースのリストアはサポートされていません。</value></data>
<data name="RestoreTaskName"><value>データベースのリストア</value></data>
<data name="RestoreCopyOnly"><value>(コピーのみ)</value></data>
<data name="RestoreBackupSetComponent"><value>コンポーネント</value></data>
<data name="RestoreBackupSetType"><value>種類</value></data>
<data name="RestoreBackupSetServer"><value>サーバー</value></data>
@@ -473,7 +475,16 @@ Enabledprototype_db_prop_parameterization = Parameterization</value></data>
<data name="TaskInProgress"><value>実行中</value></data>
<data name="TaskCompleted"><value>完了</value></data>
<data name="ScriptTaskName"><value>スクリプト</value></data>
<data name="BackupPathIsFolderError"><value>指定されたファイル名はディレクトリ名と重複します: {0}</value></data>
<data name="InvalidBackupPathError"><value>バックアップ ファイルの場所が存在するかどうかを確認できません: {0}</value></data>
<data name="ProfilerConnectionNotFound"><value>接続が見つかりません。</value></data>
<data name="BackupPathIsFolderError"><value>指定されたファイル名がディレクトリ名と同じです: {0}</value></data>
<data name="InvalidBackupPathError"><value>バックアップ ファイルの場所が存在するかどうか確認できません: {0}</value></data>
<data name="InvalidPathError"><value>サーバーで指定されたパスにアクセスできません: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>リストアするバックアップセットが選択されていません</value></data>
<data name="NeverBackedUp"><value>行わない</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>パス {0} は有効なディレクトリではありません</value></data>
<data name="Error_ExistingDirectoryName"><value>ディレクトリ {0} 内に {1} という名前のファイルは既に存在します</value></data>
<data name="EditDataValueTooLarge"><value>値 {0} は大きすぎるため、型 {1} の列に収まりません</value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>쿼리를 취소 했습니다.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>일괄 처리를 실행 하는 동안 오류가 발생 합니다.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>일괄 처리를 실행 하는 동안 오류가 발생했으나 그 오류는 무시되었습니다.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>{0} 번 루프 실행을 시작 하는 중...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>{0} 번 루프 실행을 시작 하는 중...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>{0} 명령은 지원되지 않습니다.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>{0} 변수를 찾을 수 없습니다.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>SQL 실행 오류: {0}</value></data>
@@ -480,4 +480,12 @@ votes
<data name="BackupPathIsFolderError"><value>지정한 파일 이름은 디렉터리 이름이기도 합니다: {0}</value></data>
<data name="InvalidBackupPathError"><value>백업 파일 위치를 확인할 수 없습니다: {0}</value></data>
<data name="InvalidPathError"><value>서버에서 지정된 경로에 액세스할 수 없습니다: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>복원하려는 백업 세트를 선택하지 않았습니다</value></data>
<data name="NeverBackedUp"><value>안 함 </value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL 신축성 데이터베이스</value></data>
<data name="Error_InvalidDirectoryName"><value>경로 [{0}]은(는) 올바른 디렉터리가 아닙니다.</value></data>
<data name="Error_ExistingDirectoryName"><value>디렉터리 {0}에 대한 파일{1} 이 이미 존재합니다. </value></data>
<data name="EditDataValueTooLarge"><value>값 {0}이 너무 커서 {1} 유형의 열에 들어갈 수 없습니다.</value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>Você cancelou a consulta.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>Ocorreu um erro enquanto o lote estava sendo executado.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>Ocorreu um erro enquanto o lote estava sendo executado, mas o erro foi ignorado.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>Iniciando a execução do loop {0} vezes...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>Iniciando a execução do loop {0} vezes...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>Comando {0} não é suportado.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>A variável {0} não pôde ser encontrada.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Erro de execução de SQL: {0}</value></data>
@@ -479,4 +479,12 @@
<data name="BackupPathIsFolderError"><value>O nome do arquivo especificado também é um nome de diretório: {0}</value></data>
<data name="InvalidBackupPathError"><value>Não foi possível verificar a existência do local do arquivo de backup: {0}</value></data>
<data name="InvalidPathError"><value>Não foi possível acessar o diretório especificado no servidor: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>Nenhum conjunto de backup selecionado para ser restaurado</value></data>
<data name="NeverBackedUp"><value>Nunca</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL Data Warehouse</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>Caminho {0} não é um diretório válido </value></data>
<data name="Error_ExistingDirectoryName"><value>Já existe um arquivo com nome {1} para o diretório {0}</value></data>
<data name="EditDataValueTooLarge"><value>Valor {0} é muito grande para caber em uma coluna do tipo {1}</value></data>
</root>

View File

@@ -537,7 +537,7 @@
<comment></comment>
</data>
<data name="EE_ExecutionInfo_FinalizingLoop" xml:space="preserve">
<value>Execution completed {0} times...</value>
<value>Batch execution completed {0} times...</value>
<comment></comment>
</data>
<data name="EE_ExecutionInfo_QueryCancelledbyUser" xml:space="preserve">
@@ -552,8 +552,8 @@
<value>An error occurred while the batch was being executed, but the error has been ignored.</value>
<comment></comment>
</data>
<data name="EE_ExecutionInfo_InitilizingLoop" xml:space="preserve">
<value>Starting execution loop of {0} times...</value>
<data name="EE_ExecutionInfo_InitializingLoop" xml:space="preserve">
<value>Beginning execution loop</value>
<comment></comment>
</data>
<data name="EE_ExecutionError_CommandNotSupported" xml:space="preserve">

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>Пользователь отменил запрос.</value></data>
<data name="EE_BatchExecutionError_Halting"><value>При выполнении пакета произошла ошибка.</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>В процессе выполнения пакета произошла ошибка, но она была проигнорирована.</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>Начало цикла выполнения {0} раз...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>Начало цикла выполнения {0} раз...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>Команда {0} не поддерживается.</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>Переменная {0} не найдена.</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>Ошибка выполнения SQL: {0}</value></data>
@@ -478,4 +478,6 @@
<data name="BackupPathIsFolderError"><value>Указанное имя файла является также именем каталога: {0}</value></data>
<data name="InvalidBackupPathError"><value>Невозможно проверить существование расположения файла резервной копии: {0}</value></data>
<data name="InvalidPathError"><value>Указанный путь на сервере недоступен: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>Для восстановления не выбран резервный набор данных</value></data>
<data name="NeverBackedUp"><value>Никогда </value></data>
</root>

View File

@@ -255,7 +255,7 @@ EE_ScriptError_ParsingSyntax = Incorrect syntax was encountered while {0} was be
EE_ScriptError_FatalError = A fatal error occurred.
EE_ExecutionInfo_FinalizingLoop = Execution completed {0} times...
EE_ExecutionInfo_FinalizingLoop = Batch execution completed {0} times...
EE_ExecutionInfo_QueryCancelledbyUser = You cancelled the query.
@@ -263,7 +263,7 @@ EE_BatchExecutionError_Halting = An error occurred while the batch was being exe
EE_BatchExecutionError_Ignoring = An error occurred while the batch was being executed, but the error has been ignored.
EE_ExecutionInfo_InitilizingLoop = Starting execution loop of {0} times...
EE_ExecutionInfo_InitializingLoop = Beginning execution loop
EE_ExecutionError_CommandNotSupported = Command {0} is not supported.

View File

@@ -315,7 +315,7 @@
<note></note>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_FinalizingLoop">
<source>Execution completed {0} times...</source>
<source>Batch execution completed {0} times...</source>
<target state="new">Execution completed {0} times...</target>
<note></note>
</trans-unit>
@@ -334,8 +334,8 @@
<target state="new">An error occurred while the batch was being executed, but the error has been ignored.</target>
<note></note>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<source>Starting execution loop of {0} times...</source>
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Beginning execution loop</source>
<target state="new">Starting execution loop of {0} times...</target>
<note></note>
</trans-unit>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>您已取消查询。</value></data>
<data name="EE_BatchExecutionError_Halting"><value>执行批次处理时发生错误。</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>执行批次处理时发生错误,但该错误已被忽略。</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>正在开始执行循环的次数为 {0} 次...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>正在开始执行循环的次数为 {0} 次...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>不支持命令 {0}。</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>找不到变量 {0}。</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>SQL 执行错误︰ {0}</value></data>
@@ -475,7 +475,15 @@
<data name="TaskCompleted"><value>已完成</value></data>
<data name="ScriptTaskName"><value>开始执行脚本操作</value></data>
<data name="ProfilerConnectionNotFound"><value>找不到连接</value></data>
<data name="BackupPathIsFolderError"><value>指定的文件名也是一个目录名: {0}</value></data>
<data name="InvalidBackupPathError"><value>无法验证备份文件位置是否存在: {0}</value></data>
<data name="BackupPathIsFolderError"><value>指定的文件名同时也是一个文件目录名: {0}</value></data>
<data name="InvalidBackupPathError"><value>无法验证备份文件位置是否存在: {0}</value></data>
<data name="InvalidPathError"><value>无法访问服务器上的指定路径: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>未选择用于还原的备份集</value></data>
<data name="NeverBackedUp"><value>从不</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL 数据库</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL 数据仓库</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL Stretch Database</value></data>
<data name="Error_InvalidDirectoryName"><value>路径 {0} 不是有效的目录</value></data>
<data name="Error_ExistingDirectoryName"><value>{0} 文件夹中已存在名为 {1} 的文件</value></data>
<data name="EditDataValueTooLarge"><value>值 {0} 太大,无法放入类型为 {1} 的列</value></data>
</root>

View File

@@ -91,7 +91,7 @@
<data name="EE_ExecutionInfo_QueryCancelledbyUser"><value>您已取消查詢。</value></data>
<data name="EE_BatchExecutionError_Halting"><value>執行此批次時發生錯誤。</value></data>
<data name="EE_BatchExecutionError_Ignoring"><value>執行批次,但被忽略的錯誤時,就會發生錯誤。</value></data>
<data name="EE_ExecutionInfo_InitilizingLoop"><value>正在啟動 {0} 次執行迴圈...</value></data>
<data name="EE_ExecutionInfo_InitializingLoop"><value>正在啟動 {0} 次執行迴圈...</value></data>
<data name="EE_ExecutionError_CommandNotSupported"><value>不支援命令 {0}。</value></data>
<data name="EE_ExecutionError_VariableNotFound"><value>找不到變數 {0}。</value></data>
<data name="BatchParserWrapperExecutionEngineError"><value>SQL 執行錯誤︰ {0}</value></data>
@@ -480,4 +480,12 @@ ANSI Padding 已啟用</value></data>
<data name="BackupPathIsFolderError"><value>指定的檔案名稱也是目錄名稱: {0}</value></data>
<data name="InvalidBackupPathError"><value>無法確認備份檔案位置的存在: {0}</value></data>
<data name="InvalidPathError"><value>無法存取伺服器上指定的路徑: {0}</value></data>
<data name="NoBackupsetsToRestore"><value>無選擇的備份集可還原</value></data>
<data name="NeverBackedUp"><value>永不</value></data>
<data name="AzureSqlDbEdition"><value>Azure SQL DB</value></data>
<data name="AzureSqlDwEdition"><value>Azure SQL 資料倉儲</value></data>
<data name="AzureSqlStretchEdition"><value>Azure SQL 延展資料庫</value></data>
<data name="Error_InvalidDirectoryName"><value>路徑 {0} 不是有效的目錄</value></data>
<data name="Error_ExistingDirectoryName"><value>因目錄 {0} 中已有存在的檔案名稱 {1}</value></data>
<data name="EditDataValueTooLarge"><value>數值 {0} 太大以致於無法符合欄位型態 {1}</value></data>
</root>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">{0}-malige Batchausführung wurde gestartet.</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">Iniciando bucle de ejecución de {0} veces...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">Démarrage de la boucle d'exécution pour {0} fois...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">Avvio ciclo di esecuzione di {0} volte...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">{0} 回の実行ループを開始しています.</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">{0} 번 루프 실행을 시작 하는 중...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">Iniciando a execução do loop {0} vezes...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">Начало цикла выполнения {0} раз...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">正在开始执行循环的次数为 {0} 次...</target>

View File

@@ -397,7 +397,7 @@
<note/>
</trans-unit>
<trans-unit id="EE_ExecutionInfo_InitilizingLoop">
<trans-unit id="EE_ExecutionInfo_InitializingLoop">
<source>Starting execution loop of {0} times...</source>
<target state="new">正在啟動 {0} 次執行迴圈...</target>

View File

@@ -15,6 +15,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.Utility;
using System.Globalization;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
@@ -61,7 +62,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
#endregion
internal Batch(string batchText, SelectionData selection, int ordinalId, IFileStreamFactory outputFileFactory)
internal Batch(string batchText, SelectionData selection, int ordinalId,
IFileStreamFactory outputFileFactory, int executionCount = 1)
{
// Sanity check for input
Validate.IsNotNullOrEmptyString(nameof(batchText), batchText);
@@ -77,6 +79,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
resultSets = new List<ResultSet>();
this.outputFileFactory = outputFileFactory;
specialAction = new SpecialAction();
BatchExecutionCount = executionCount > 0 ? executionCount : 1;
}
#region Events
@@ -123,6 +126,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// </summary>
public string BatchText { get; set; }
public int BatchExecutionCount { get; private set; }
/// <summary>
/// Localized timestamp for when the execution completed.
/// Stored in UTC ISO 8601 format; should be localized before displaying to any user
@@ -237,92 +241,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
await BatchStart(this);
}
try
{
// Make sure we haven't cancelled yet
cancellationToken.ThrowIfCancellationRequested();
// Register the message listener to *this instance* of the batch
// Note: This is being done to associate messages with batches
ReliableSqlConnection sqlConn = conn as ReliableSqlConnection;
DbCommand dbCommand;
if (sqlConn != null)
{
// Register the message listener to *this instance* of the batch
// Note: This is being done to associate messages with batches
sqlConn.GetUnderlyingConnection().InfoMessage += ServerMessageHandler;
dbCommand = sqlConn.GetUnderlyingConnection().CreateCommand();
// Add a handler for when the command completes
SqlCommand sqlCommand = (SqlCommand)dbCommand;
sqlCommand.StatementCompleted += StatementCompletedHandler;
}
else
{
dbCommand = conn.CreateCommand();
}
// Make sure we aren't using a ReliableCommad since we do not want automatic retry
Debug.Assert(!(dbCommand is ReliableSqlConnection.ReliableSqlCommand),
"ReliableSqlCommand command should not be used to execute queries");
// Create a command that we'll use for executing the query
using (dbCommand)
{
// Make sure that we cancel the command if the cancellation token is cancelled
cancellationToken.Register(() => dbCommand?.Cancel());
// Setup the command for executing the batch
dbCommand.CommandText = BatchText;
dbCommand.CommandType = CommandType.Text;
dbCommand.CommandTimeout = 0;
executionStartTime = DateTime.Now;
// Execute the command to get back a reader
using (DbDataReader reader = await dbCommand.ExecuteReaderAsync(cancellationToken))
{
int resultSetOrdinal = 0;
do
{
// Verify that the cancellation token hasn't benn cancelled
cancellationToken.ThrowIfCancellationRequested();
// Skip this result set if there aren't any rows (ie, UPDATE/DELETE/etc queries)
if (!reader.HasRows && reader.FieldCount == 0)
{
continue;
}
// This resultset has results (ie, SELECT/etc queries)
ResultSet resultSet = new ResultSet(resultSetOrdinal, Id, outputFileFactory);
resultSet.ResultCompletion += ResultSetCompletion;
// Add the result set to the results of the query
lock (resultSets)
{
resultSets.Add(resultSet);
resultSetOrdinal++;
}
// Read until we hit the end of the result set
await resultSet.ReadResultToEnd(reader, cancellationToken);
} while (await reader.NextResultAsync(cancellationToken));
// If there were no messages, for whatever reason (NO COUNT set, messages
// were emitted, records returned), output a "successful" message
if (!messagesSent)
{
await SendMessage(SR.QueryServiceCompletedSuccessfully, false);
}
}
}
}
catch (DbException dbe)
{
HasError = true;
await UnwrapDbException(dbe);
await DoExecute(conn, cancellationToken);
}
catch (TaskCanceledException)
{
@@ -355,6 +277,131 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
await BatchCompletion(this);
}
}
}
private async Task DoExecute(DbConnection conn, CancellationToken cancellationToken)
{
bool canContinue = true;
int timesLoop = this.BatchExecutionCount;
await SendMessageIfExecutingMultipleTimes(SR.EE_ExecutionInfo_InitializingLoop, false);
while (canContinue && timesLoop > 0)
{
try
{
await ExecuteOnce(conn, cancellationToken);
}
catch (DbException dbe)
{
HasError = true;
canContinue = await UnwrapDbException(dbe);
if (canContinue)
{
// If it's a multi-batch, we notify the user that we're ignoring a single failure.
await SendMessageIfExecutingMultipleTimes(SR.EE_BatchExecutionError_Ignoring, false);
}
}
timesLoop--;
}
await SendMessageIfExecutingMultipleTimes(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionInfo_FinalizingLoop, this.BatchExecutionCount), false);
}
private async Task SendMessageIfExecutingMultipleTimes(string message, bool isError)
{
if (IsExecutingMultipleTimes())
{
await SendMessage(message, isError);
}
}
private bool IsExecutingMultipleTimes()
{
return this.BatchExecutionCount > 1;
}
private async Task ExecuteOnce(DbConnection conn, CancellationToken cancellationToken)
{
// Make sure we haven't cancelled yet
cancellationToken.ThrowIfCancellationRequested();
// Register the message listener to *this instance* of the batch
// Note: This is being done to associate messages with batches
ReliableSqlConnection sqlConn = conn as ReliableSqlConnection;
DbCommand dbCommand;
if (sqlConn != null)
{
// Register the message listener to *this instance* of the batch
// Note: This is being done to associate messages with batches
sqlConn.GetUnderlyingConnection().InfoMessage += ServerMessageHandler;
dbCommand = sqlConn.GetUnderlyingConnection().CreateCommand();
// Add a handler for when the command completes
SqlCommand sqlCommand = (SqlCommand)dbCommand;
sqlCommand.StatementCompleted += StatementCompletedHandler;
}
else
{
dbCommand = conn.CreateCommand();
}
// Make sure we aren't using a ReliableCommad since we do not want automatic retry
Debug.Assert(!(dbCommand is ReliableSqlConnection.ReliableSqlCommand),
"ReliableSqlCommand command should not be used to execute queries");
// Create a command that we'll use for executing the query
using (dbCommand)
{
// Make sure that we cancel the command if the cancellation token is cancelled
cancellationToken.Register(() => dbCommand?.Cancel());
// Setup the command for executing the batch
dbCommand.CommandText = BatchText;
dbCommand.CommandType = CommandType.Text;
dbCommand.CommandTimeout = 0;
executionStartTime = DateTime.Now;
// Execute the command to get back a reader
using (DbDataReader reader = await dbCommand.ExecuteReaderAsync(cancellationToken))
{
int resultSetOrdinal = 0;
do
{
// Verify that the cancellation token hasn't benn cancelled
cancellationToken.ThrowIfCancellationRequested();
// Skip this result set if there aren't any rows (ie, UPDATE/DELETE/etc queries)
if (!reader.HasRows && reader.FieldCount == 0)
{
continue;
}
// This resultset has results (ie, SELECT/etc queries)
ResultSet resultSet = new ResultSet(resultSetOrdinal, Id, outputFileFactory);
resultSet.ResultCompletion += ResultSetCompletion;
// Add the result set to the results of the query
lock (resultSets)
{
resultSets.Add(resultSet);
resultSetOrdinal++;
}
// Read until we hit the end of the result set
await resultSet.ReadResultToEnd(reader, cancellationToken);
} while (await reader.NextResultAsync(cancellationToken));
// If there were no messages, for whatever reason (NO COUNT set, messages
// were emitted, records returned), output a "successful" message
if (!messagesSent)
{
await SendMessage(SR.QueryServiceCompletedSuccessfully, false);
}
}
}
}
/// <summary>
@@ -487,8 +534,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// converted to SqlException, the message is written to the messages list.
/// </summary>
/// <param name="dbe">The exception to unwrap</param>
private async Task UnwrapDbException(Exception dbe)
/// <returns>true is exception can be ignored when in a loop, false otherwise</returns>
private async Task<bool> UnwrapDbException(Exception dbe)
{
bool canIgnore = true;
SqlException se = dbe as SqlException;
if (se != null)
{
@@ -499,6 +548,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
// User cancellation error, add the single message
await SendMessage(SR.QueryServiceQueryCancelled, false);
canIgnore = false;
}
else
{
@@ -517,6 +567,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
await SendMessage(dbe.Message, true);
}
return canIgnore;
}
/// <summary>

View File

@@ -118,7 +118,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
batchDefinition.StartColumn-1,
batchDefinition.EndLine-1,
batchDefinition.EndColumn-1),
index, outputFactory));
index, outputFactory,
batchDefinition.BatchExecutionCount));
Batches = batchSelection.ToArray();
@@ -462,7 +463,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// </summary>
private static void AddBatch(string query, ICollection<Batch> batchSet, IFileStreamFactory outputFactory)
{
batchSet.Add(new Batch(query, null, batchSet.Count, outputFactory));
batchSet.Add(new Batch(query, null, batchSet.Count, outputFactory, 1));
}
#endregion

View File

@@ -1,4 +1,5 @@
using Microsoft.SqlTools.ServiceLayer.BatchParser;
using System;
using Microsoft.SqlTools.ServiceLayer.BatchParser;
using Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode;
using Xunit;
@@ -20,6 +21,20 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.BatchParser
Assert.Equal(1, batch.StartColumn);
Assert.Equal(2, batch.EndLine);
Assert.Equal(sqlScript.Length+1, batch.EndColumn);
Assert.Equal(1, batch.BatchExecutionCount);
}
}
[Fact]
public void CheckSQLBatchStatementWithRepeatExecution()
{
using (BatchParserWrapper parserWrapper = new BatchParserWrapper())
{
string sqlScript = "select * from sys.object" + Environment.NewLine + "GO 2";
var batches = parserWrapper.GetBatches(sqlScript);
Assert.Equal(1, batches.Count);
BatchDefinition batch = batches[0];
Assert.Equal(2, batch.BatchExecutionCount);
}
}

View File

@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
@@ -157,6 +158,43 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution
ValidateMessages(batch, 1, messages);
}
[Fact]
public async Task BatchExecuteMultiExecutions()
{
// Setup:
// ... Keep track of callbacks being called
int batchStartCalls = 0;
int batchEndCalls = 0;
int resultSetCalls = 0;
List<ResultMessage> messages = new List<ResultMessage>();
// ... Build a data set to return
const int resultSets = 1;
ConnectionInfo ci = Common.CreateTestConnectionInfo(Common.GetTestDataSet(resultSets), false, false);
// If I execute a query that should get one result set, but execute it twice using "GO 2" syntax
var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory();
Batch batch = new Batch(Constants.StandardQuery, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory, 2);
BatchCallbackHelper(batch,
b => batchStartCalls++,
b => batchEndCalls++,
m => messages.Add(m),
r => resultSetCalls++);
await batch.Execute(GetConnection(ci), CancellationToken.None);
// Then:
// ... Callbacks should have been called the appropriate number of times
Assert.Equal(1, batchStartCalls);
Assert.Equal(1, batchEndCalls);
Assert.Equal(2, resultSetCalls);
// ... There should be exactly two result sets
ValidateBatch(batch, 2, false);
ValidateBatchSummary(batch);
// ... And there should be an additional loop start in addition to the batch end message
ValidateMessages(batch, 2, messages);
}
[Fact]
public async Task BatchExecuteInvalidQuery()
{
@@ -195,6 +233,44 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.Execution
});
}
[Fact]
public async Task BatchExecuteInvalidQueryMultiExecutions()
{
// Setup:
// ... Keep track of callbacks being called
int batchStartCalls = 0;
int batchEndCalls = 0;
List<ResultMessage> messages = new List<ResultMessage>();
// If I execute a batch that is invalid, and if "GO 2" is added to execute more than once
var ci = Common.CreateTestConnectionInfo(null, true, false);
var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory();
Batch batch = new Batch(Constants.StandardQuery + Environment.NewLine, Common.SubsectionDocument, Common.Ordinal, fileStreamFactory, 2);
BatchCallbackHelper(batch,
b => batchStartCalls++,
b => batchEndCalls++,
m => messages.Add(m),
r => { throw new Exception("ResultSet callback was called when it should not have been."); });
await batch.Execute(GetConnection(ci), CancellationToken.None);
// Then:
// ... Callbacks should have been called the appropriate number of times
Assert.Equal(1, batchStartCalls);
Assert.Equal(1, batchEndCalls);
// ... It should have executed with error
ValidateBatch(batch, 0, true);
ValidateBatchSummary(batch);
// ... There should be two error messages returned and 4 info messages (loop start/end, plus 2 for ignoring the error)
Assert.Equal(6, messages.Count);
Assert.All(messages, m =>
{
Assert.Equal(batch.Id, m.BatchId);
});
Assert.Equal(2, messages.Where(m => m.IsError).Count());
}
[Fact]
public async Task BatchExecuteExecuted()
{

View File

@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
Assert.NotNull(ServiceLayerSr.EE_ExecutionError_CommandNotSupported);
Assert.NotNull(ServiceLayerSr.EE_ExecutionError_VariableNotFound);
Assert.NotNull(ServiceLayerSr.EE_ExecutionInfo_FinalizingLoop);
Assert.NotNull(ServiceLayerSr.EE_ExecutionInfo_InitilizingLoop);
Assert.NotNull(ServiceLayerSr.EE_ExecutionInfo_InitializingLoop);
Assert.NotNull(ServiceLayerSr.EE_ExecutionInfo_QueryCancelledbyUser);
Assert.NotNull(ServiceLayerSr.EE_ExecutionNotYetCompleteError);
Assert.NotNull(ServiceLayerSr.EE_ScriptError_Error);