mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-24 17:24:14 -05:00
SqlCmd Connect/On Error/Include commands support (#898)
* Initial Investigation * Working code with include, connect, on error and tests * Adding some loc strings * Some cleanup and more tests * Some dummy change to trigger build * Adding PR comments * Addressing PR comments
This commit is contained in:
@@ -355,6 +355,21 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
{
|
||||
return Keys.GetString(Keys.QueryServiceExecutionPlanNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
public static string SqlCmdExitOnError
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.SqlCmdExitOnError);
|
||||
}
|
||||
}
|
||||
public static string SqlCmdUnsupportedToken
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.SqlCmdUnsupportedToken);
|
||||
}
|
||||
}
|
||||
|
||||
public static string PeekDefinitionNoResultsError
|
||||
@@ -3277,7 +3292,13 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string QueryServiceResultSetNoColumnSchema = "QueryServiceResultSetNoColumnSchema";
|
||||
|
||||
|
||||
public const string QueryServiceExecutionPlanNotFound = "QueryServiceExecutionPlanNotFound";
|
||||
public const string QueryServiceExecutionPlanNotFound = "QueryServiceExecutionPlanNotFound";
|
||||
|
||||
|
||||
public const string SqlCmdExitOnError = "SqlCmdExitOnError";
|
||||
|
||||
|
||||
public const string SqlCmdUnsupportedToken = "SqlCmdUnsupportedToken";
|
||||
|
||||
|
||||
public const string SerializationServiceUnsupportedFormat = "SerializationServiceUnsupportedFormat";
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
<?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.
|
||||
<!--
|
||||
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" />
|
||||
@@ -322,6 +322,14 @@
|
||||
<value>Could not retrieve an execution plan from the result set </value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="SqlCmdExitOnError" xml:space="preserve">
|
||||
<value>An error was encountered during execution of batch. Exiting.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="SqlCmdUnsupportedToken" xml:space="preserve">
|
||||
<value>Encountered unsupported token {0}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="SerializationServiceUnsupportedFormat" xml:space="preserve">
|
||||
<value>Unsupported Save Format: {0}</value>
|
||||
<comment>.
|
||||
|
||||
@@ -140,6 +140,10 @@ QueryServiceResultSetNoColumnSchema = Could not retrieve column schema for resul
|
||||
|
||||
QueryServiceExecutionPlanNotFound = Could not retrieve an execution plan from the result set
|
||||
|
||||
SqlCmdExitOnError = An error was encountered during execution of batch. Exiting.
|
||||
|
||||
SqlCmdUnsupportedToken = Encountered unsupported token {0}
|
||||
|
||||
############################################################################
|
||||
# Serialization Service
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ using Microsoft.SqlTools.Utility;
|
||||
using System.Globalization;
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
{
|
||||
@@ -71,7 +72,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
#endregion
|
||||
|
||||
internal Batch(string batchText, SelectionData selection, int ordinalId,
|
||||
IFileStreamFactory outputFileFactory, int executionCount = 1, bool getFullColumnSchema = false)
|
||||
IFileStreamFactory outputFileFactory, SqlCmdCommand sqlCmdCommand, int executionCount = 1, bool getFullColumnSchema = false) : this(batchText, selection, ordinalId,
|
||||
outputFileFactory, executionCount, getFullColumnSchema)
|
||||
{
|
||||
this.SqlCmdCommand = sqlCmdCommand;
|
||||
}
|
||||
|
||||
internal Batch(string batchText, SelectionData selection, int ordinalId,
|
||||
IFileStreamFactory outputFileFactory, int executionCount = 1, bool getFullColumnSchema = false)
|
||||
{
|
||||
// Sanity check for input
|
||||
Validate.IsNotNullOrEmptyString(nameof(batchText), batchText);
|
||||
@@ -138,6 +146,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// called from the Batch but from the ResultSet instance.
|
||||
/// </summary>
|
||||
public event ResultSet.ResultSetAsyncEventHandler ResultSetUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Event that will be called when additional rows in the result set are available (rowCount available has increased). It will not be
|
||||
/// called from the Batch but from the ResultSet instance.
|
||||
/// </summary>
|
||||
public event EventHandler<bool> HandleOnErrorAction;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -147,6 +161,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// </summary>
|
||||
public string BatchText { get; set; }
|
||||
|
||||
public SqlCmdCommand SqlCmdCommand { get; set; }
|
||||
|
||||
public int BatchExecutionCount { get; private set; }
|
||||
/// <summary>
|
||||
/// Localized timestamp for when the execution completed.
|
||||
@@ -243,13 +259,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes this batch and captures any server messages that are returned.
|
||||
/// </summary>
|
||||
/// <param name="conn">The connection to use to execute the batch</param>
|
||||
/// <param name="cancellationToken">Token for cancelling the execution</param>
|
||||
public async Task Execute(DbConnection conn, CancellationToken cancellationToken)
|
||||
{
|
||||
await Execute(conn, cancellationToken, OnErrorAction.Ignore);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes this batch and captures any server messages that are returned.
|
||||
/// </summary>
|
||||
/// <param name="conn">The connection to use to execute the batch</param>
|
||||
/// <param name="cancellationToken">Token for cancelling the execution</param>
|
||||
/// <param name="onErrorAction">Continue (Ignore) or Exit on Error. This comes only in SQLCMD mode</param>
|
||||
public async Task Execute(DbConnection conn, CancellationToken cancellationToken, OnErrorAction onErrorAction = OnErrorAction.Ignore)
|
||||
{
|
||||
// Sanity check to make sure we haven't already run this batch
|
||||
if (HasExecuted)
|
||||
@@ -270,10 +297,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
{
|
||||
sqlConn.GetUnderlyingConnection().InfoMessage += ServerMessageHandler;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
await DoExecute(conn, cancellationToken);
|
||||
await DoExecute(conn, cancellationToken, onErrorAction);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
@@ -308,7 +335,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
|
||||
}
|
||||
|
||||
private async Task DoExecute(DbConnection conn, CancellationToken cancellationToken)
|
||||
private async Task DoExecute(DbConnection conn, CancellationToken cancellationToken, OnErrorAction onErrorAction = OnErrorAction.Ignore)
|
||||
{
|
||||
bool canContinue = true;
|
||||
int timesLoop = this.BatchExecutionCount;
|
||||
@@ -326,6 +353,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
catch (DbException dbe)
|
||||
{
|
||||
HasError = true;
|
||||
if (onErrorAction == OnErrorAction.Exit)
|
||||
{
|
||||
throw new SqlCmdException(dbe.Message);
|
||||
}
|
||||
canContinue = await UnwrapDbException(dbe);
|
||||
if (canContinue)
|
||||
{
|
||||
@@ -445,7 +476,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
private void ExtendResultMetadata(List<DbColumn[]> columnSchemas, List<ResultSet> results)
|
||||
{
|
||||
if (columnSchemas.Count != results.Count)
|
||||
{
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -679,6 +710,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
{
|
||||
this.HasError = true;
|
||||
}
|
||||
|
||||
if (this.HandleOnErrorAction != null)
|
||||
{
|
||||
HandleOnErrorAction(this, isError);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -693,6 +729,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
{
|
||||
bool canIgnore = true;
|
||||
SqlException se = dbe as SqlException;
|
||||
|
||||
if (se != null)
|
||||
{
|
||||
var errors = se.Errors.Cast<SqlError>().ToList();
|
||||
@@ -729,7 +766,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// </summary>
|
||||
private SpecialAction ProcessResultSetSpecialActions()
|
||||
{
|
||||
foreach (ResultSet resultSet in resultSets)
|
||||
foreach (ResultSet resultSet in resultSets)
|
||||
{
|
||||
specialAction.CombineSpecialAction(resultSet.Summary.SpecialAction);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
public class Query : IDisposable
|
||||
{
|
||||
#region Constants
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// "Error" code produced by SQL Server when the database context (name) for a connection changes.
|
||||
/// </summary>
|
||||
private const int DatabaseContextChangeErrorNumber = 5701;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ON keyword
|
||||
/// </summary>
|
||||
@@ -45,7 +45,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// OFF keyword
|
||||
/// </summary>
|
||||
private const string Off = "OFF";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// showplan_xml statement
|
||||
/// </summary>
|
||||
@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// statistics xml statement
|
||||
/// </summary>
|
||||
private const string SetStatisticsXml = "SET STATISTICS XML {0}";
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Member Variables
|
||||
@@ -84,7 +84,23 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// <summary>
|
||||
/// Name of the new database if the database name was changed in the query
|
||||
/// </summary>
|
||||
private string newDatabaseName;
|
||||
private string newDatabaseName;
|
||||
|
||||
/// <summary>
|
||||
/// On Error Action for the query in SQLCMD Mode- Ignore or Exit
|
||||
/// </summary>
|
||||
private OnErrorAction onErrorAction;
|
||||
|
||||
/// <summary>
|
||||
/// Connection that is used for query to run
|
||||
/// This is always initialized from editor connection but might be different in case of SQLCMD mode
|
||||
/// </summary>
|
||||
private DbConnection queryConnection;
|
||||
|
||||
/// <summary>
|
||||
/// Cancelled but not user but by SQLCMD settings
|
||||
/// </summary>
|
||||
private bool CancelledBySqlCmd;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -96,9 +112,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// <param name="settings">Settings for how to execute the query, from the user</param>
|
||||
/// <param name="outputFactory">Factory for creating output files</param>
|
||||
public Query(
|
||||
string queryText,
|
||||
ConnectionInfo connection,
|
||||
QueryExecutionSettings settings,
|
||||
string queryText,
|
||||
ConnectionInfo connection,
|
||||
QueryExecutionSettings settings,
|
||||
IFileStreamFactory outputFactory,
|
||||
bool getFullColumnSchema = false,
|
||||
bool applyExecutionSettings = false)
|
||||
@@ -125,13 +141,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
|
||||
var batchSelection = parserResult
|
||||
.Select((batchDefinition, index) =>
|
||||
new Batch(batchDefinition.BatchText,
|
||||
new Batch(batchDefinition.BatchText,
|
||||
new SelectionData(
|
||||
batchDefinition.StartLine-1,
|
||||
batchDefinition.StartColumn-1,
|
||||
batchDefinition.EndLine-1,
|
||||
batchDefinition.EndColumn-1),
|
||||
batchDefinition.EndColumn-1),
|
||||
index, outputFactory,
|
||||
batchDefinition.SqlCmdCommand,
|
||||
batchDefinition.BatchExecutionCount,
|
||||
getFullColumnSchema));
|
||||
|
||||
@@ -154,14 +171,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// </summary>
|
||||
/// <param name="query">The query that completed</param>
|
||||
public delegate Task QueryAsyncEventHandler(Query query);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delegate type for callback when a query fails
|
||||
/// </summary>
|
||||
/// <param name="query">Query that raised the event</param>
|
||||
/// <param name="exception">Exception that caused the query to fail</param>
|
||||
public delegate Task QueryAsyncErrorEventHandler(Query query, Exception exception);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event to be called when a batch is completed.
|
||||
/// </summary>
|
||||
@@ -356,7 +373,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// </param>
|
||||
/// <param name="successHandler">Delegate to call when the request completes successfully</param>
|
||||
/// <param name="failureHandler">Delegate to call if the request fails</param>
|
||||
public void SaveAs(SaveResultsRequestParams saveParams, IFileStreamFactory fileFactory,
|
||||
public void SaveAs(SaveResultsRequestParams saveParams, IFileStreamFactory fileFactory,
|
||||
ResultSet.SaveAsAsyncEventHandler successHandler, ResultSet.SaveAsFailureAsyncEventHandler failureHandler)
|
||||
{
|
||||
// Sanity check to make sure that the batch is within bounds
|
||||
@@ -385,7 +402,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
|
||||
// Mark that we've internally executed
|
||||
hasExecuteBeenCalled = true;
|
||||
|
||||
|
||||
// Don't actually execute if there aren't any batches to execute
|
||||
if (Batches.Length == 0)
|
||||
{
|
||||
@@ -399,9 +416,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Locate and setup the connection
|
||||
DbConnection queryConnection = await ConnectionService.Instance.GetOrOpenConnection(editorConnection.OwnerUri, ConnectionType.Query);
|
||||
queryConnection = await ConnectionService.Instance.GetOrOpenConnection(editorConnection.OwnerUri, ConnectionType.Query);
|
||||
onErrorAction = OnErrorAction.Ignore;
|
||||
sqlConn = queryConnection as ReliableSqlConnection;
|
||||
if (sqlConn != null)
|
||||
{
|
||||
@@ -410,7 +428,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
sqlConn.GetUnderlyingConnection().InfoMessage += OnInfoMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Execute beforeBatches synchronously, before the user defined batches
|
||||
foreach (Batch b in BeforeBatches)
|
||||
{
|
||||
@@ -427,7 +445,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
b.ResultSetCompletion += ResultSetCompleted;
|
||||
b.ResultSetAvailable += ResultSetAvailable;
|
||||
b.ResultSetUpdated += ResultSetUpdated;
|
||||
await b.Execute(queryConnection, cancellationSource.Token);
|
||||
|
||||
await ExecuteBatch(b);
|
||||
}
|
||||
|
||||
// Execute afterBatches synchronously, after the user defined batches
|
||||
@@ -445,7 +464,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
catch (Exception e)
|
||||
{
|
||||
HasErrored = true;
|
||||
if (e is OperationCanceledException)
|
||||
if (e is SqlCmdException || CancelledBySqlCmd)
|
||||
{
|
||||
await BatchMessageSent(new ResultMessage(SR.SqlCmdExitOnError, false, null));
|
||||
}
|
||||
else if (e is OperationCanceledException)
|
||||
{
|
||||
await BatchMessageSent(new ResultMessage(SR.QueryServiceQueryCancelled, false, null));
|
||||
}
|
||||
@@ -463,7 +486,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
// Subscribe to database informational messages
|
||||
sqlConn.GetUnderlyingConnection().InfoMessage -= OnInfoMessage;
|
||||
}
|
||||
|
||||
|
||||
// If any message notified us we had changed databases, then we must let the connection service know
|
||||
if (newDatabaseName != null)
|
||||
{
|
||||
@@ -480,6 +503,63 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
}
|
||||
}
|
||||
}
|
||||
private async Task ExecuteBatch(Batch b)
|
||||
{
|
||||
if (b.SqlCmdCommand != null)
|
||||
{
|
||||
b.HandleOnErrorAction += HandleOnErrorAction;
|
||||
await PerformInplaceSqlCmdAction(b);
|
||||
}
|
||||
|
||||
await b.Execute(queryConnection, cancellationSource.Token, this.onErrorAction);
|
||||
|
||||
if (CancelledBySqlCmd)
|
||||
{
|
||||
throw new SqlCmdException(SR.SqlCmdExitOnError);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PerformInplaceSqlCmdAction(Batch b)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (b.SqlCmdCommand.LexerTokenType)
|
||||
{
|
||||
case LexerTokenType.Connect:
|
||||
var qc = (b.SqlCmdCommand as ConnectSqlCmdCommand)?.Connect();
|
||||
queryConnection = qc ?? queryConnection;
|
||||
break;
|
||||
case LexerTokenType.OnError:
|
||||
onErrorAction = (b.SqlCmdCommand as OnErrorSqlCmdCommand).Action;
|
||||
break;
|
||||
default:
|
||||
throw new SqlCmdException(string.Format(SR.SqlCmdUnsupportedToken, b.SqlCmdCommand.LexerTokenType));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
b.HasError = true;
|
||||
await BatchMessageSent(new ResultMessage(ex.Message, true, null));
|
||||
if (this.onErrorAction == OnErrorAction.Exit)
|
||||
{
|
||||
HasCancelled = true;
|
||||
CancelledBySqlCmd = true;
|
||||
cancellationSource.Cancel();
|
||||
throw new SqlCmdException(SR.SqlCmdExitOnError);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOnErrorAction(object sender, bool iserror)
|
||||
{
|
||||
if (iserror && this.onErrorAction == OnErrorAction.Exit)
|
||||
{
|
||||
HasCancelled = true;
|
||||
CancelledBySqlCmd = true;
|
||||
cancellationSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for database messages during query execution
|
||||
@@ -511,8 +591,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
}
|
||||
|
||||
private void ApplyExecutionSettings(
|
||||
ConnectionInfo connection,
|
||||
QueryExecutionSettings settings,
|
||||
ConnectionInfo connection,
|
||||
QueryExecutionSettings settings,
|
||||
IFileStreamFactory outputFactory)
|
||||
{
|
||||
outputFactory.QueryExecutionSettings = settings;
|
||||
@@ -549,7 +629,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
}
|
||||
|
||||
if (settings.StatisticsIO)
|
||||
{
|
||||
{
|
||||
builderBefore.AppendFormat("{0} ", helper.GetSetStatisticsIOString(true));
|
||||
builderAfter.AppendFormat("{0} ", helper.GetSetStatisticsIOString (false));
|
||||
}
|
||||
@@ -562,35 +642,35 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
}
|
||||
|
||||
if (settings.ParseOnly)
|
||||
{
|
||||
{
|
||||
builderBefore.AppendFormat("{0} ", helper.GetSetParseOnlyString(true));
|
||||
builderAfter.AppendFormat("{0} ", helper.GetSetParseOnlyString(false));
|
||||
}
|
||||
|
||||
// append first part of exec options
|
||||
builderBefore.AppendFormat("{0} {1} {2}",
|
||||
builderBefore.AppendFormat("{0} {1} {2}",
|
||||
helper.SetRowCountString, helper.SetTextSizeString, helper.SetNoCountString);
|
||||
|
||||
if (!connection.IsSqlDW)
|
||||
{
|
||||
// append second part of exec options
|
||||
builderBefore.AppendFormat(" {0} {1} {2} {3} {4} {5} {6}",
|
||||
helper.SetConcatenationNullString,
|
||||
helper.SetArithAbortString,
|
||||
helper.SetLockTimeoutString,
|
||||
helper.SetQueryGovernorCostString,
|
||||
helper.SetDeadlockPriorityString,
|
||||
helper.SetConcatenationNullString,
|
||||
helper.SetArithAbortString,
|
||||
helper.SetLockTimeoutString,
|
||||
helper.SetQueryGovernorCostString,
|
||||
helper.SetDeadlockPriorityString,
|
||||
helper.SetTransactionIsolationLevelString,
|
||||
// We treat XACT_ABORT special in that we don't add anything if the option
|
||||
// isn't checked. This is because we don't want to be overwriting the server
|
||||
// if it has a default of ON since that's something people would specifically
|
||||
// set and having a client change it could be dangerous (the reverse is much
|
||||
// less risky)
|
||||
|
||||
|
||||
// The full fix would probably be to make the options tri-state instead of
|
||||
// just on/off, where the default is to use the servers default. Until that
|
||||
// happens though this is the best solution we came up with. See TFS#7937925
|
||||
|
||||
|
||||
// Note that users can always specifically add SET XACT_ABORT OFF to their
|
||||
// queries if they do truly want to set it off. We just don't want to
|
||||
// do it silently (since the default is going to be off)
|
||||
@@ -655,7 +735,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
/// <summary>
|
||||
/// Does this connection support XML Execution plans
|
||||
/// </summary>
|
||||
private bool DoesSupportExecutionPlan(ConnectionInfo connectionInfo)
|
||||
private bool DoesSupportExecutionPlan(ConnectionInfo connectionInfo)
|
||||
{
|
||||
// Determining which execution plan options may be applied (may be added to for pre-yukon support)
|
||||
return (!connectionInfo.IsSqlDW && connectionInfo.MajorVersion >= 9);
|
||||
|
||||
Reference in New Issue
Block a user