Move managed parser into its own project (test code coverage) (#774)

* Created New ManagedBatchParser project in .NetStandard

* Addressing PR Comments

* Resolve 'No Repository' warning.

* Move batch parser tests to integrations test project

* Fix SLN file
This commit is contained in:
Karl Burtram
2019-02-07 20:13:03 -08:00
committed by GitHub
parent 0a172f3c8e
commit 022282800a
92 changed files with 2471 additions and 6391 deletions

View File

@@ -0,0 +1,892 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.ManagedBatchParser;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Single batch of SQL command
/// </summary>
public class Batch
{
#region Private methods
/// <summary>
/// Helper method to format the provided SqlError
/// </summary>
/// <param name="error"></param>
/// <returns></returns>
private string FormatSqlErrorMessage(SqlError error)
{
string detailedMessage = string.Empty;
if (error.Class > 10)
{
if (string.IsNullOrEmpty(error.Procedure))
{
detailedMessage = string.Format(CultureInfo.CurrentCulture, SR.EE_BatchSqlMessageNoProcedureInfo,
error.Number,
error.Class,
error.State,
error.LineNumber);
}
else
{
detailedMessage = string.Format(CultureInfo.CurrentCulture, SR.EE_BatchSqlMessageWithProcedureInfo,
error.Number,
error.Class,
error.State,
error.Procedure,
error.LineNumber);
}
}
else if (error.Class > 0 && error.Number > 0)
{
detailedMessage = string.Format(CultureInfo.CurrentCulture, SR.EE_BatchSqlMessageNoLineInfo,
error.Number,
error.Class,
error.State);
}
if (!string.IsNullOrEmpty(detailedMessage) && !isSuppressProviderMessageHeaders)
{
detailedMessage = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", error.Source, detailedMessage);
}
return detailedMessage;
}
/// <summary>
/// Handles a Sql exception
/// </summary>
/// <param name="ex"></param>
/// <returns></returns>
private ScriptExecutionResult HandleSqlException(SqlException ex)
{
ScriptExecutionResult result;
lock (this)
{
if (state == BatchState.Cancelling)
{
result = ScriptExecutionResult.Cancel;
}
else
{
result = ScriptExecutionResult.Failure;
}
}
if (result != ScriptExecutionResult.Cancel)
{
HandleSqlMessages(ex.Errors);
}
return result;
}
/// <summary>
/// Called when an error message came from SqlClient
/// </summary>
/// <param name="message"></param>
/// <param name="description"></param>
/// <param name="line"></param>
/// <param name="textSpan"></param>
private void RaiseBatchError(string message, SqlError error, TextSpan textSpan)
{
BatchErrorEventArgs args = new BatchErrorEventArgs(message, error, textSpan, null);
RaiseBatchError(args);
}
/// <summary>
/// Called when an error message came from SqlClient
/// </summary>
/// <param name="e"></param>
private void RaiseBatchError(BatchErrorEventArgs e)
{
EventHandler<BatchErrorEventArgs> cache = BatchError;
if (cache != null)
{
cache(this, e);
}
}
/// <summary>
/// Called when a message came from SqlClient
/// </summary>
/// <remarks>
/// Additionally, it's being used to notify the user that the script execution
/// has been finished.
/// </remarks>
/// <param name="detailedMessage"></param>
/// <param name="message"></param>
private void RaiseBatchMessage(string detailedMessage, string message, SqlError error)
{
EventHandler<BatchMessageEventArgs> cache = BatchMessage;
if (cache != null)
{
BatchMessageEventArgs args = new BatchMessageEventArgs(detailedMessage, message, error);
cache(this, args);
}
}
/// <summary>
/// Called when a new result set has to be processed
/// </summary>
/// <param name="resultSet"></param>
private void RaiseBatchResultSetProcessing(IDataReader dataReader, ShowPlanType expectedShowPlan)
{
EventHandler<BatchResultSetEventArgs> cache = BatchResultSetProcessing;
if (cache != null)
{
BatchResultSetEventArgs args = new BatchResultSetEventArgs(dataReader, expectedShowPlan);
BatchResultSetProcessing(this, args);
}
}
/// <summary>
/// Called when the result set has been processed
/// </summary>
private void RaiseBatchResultSetFinished()
{
EventHandler<EventArgs> cache = BatchResultSetFinished;
if (cache != null)
{
cache(this, EventArgs.Empty);
}
}
/// <summary>
/// Called when the batch is being cancelled with an active result set
/// </summary>
private void RaiseCancelling()
{
EventHandler<EventArgs> cache = BatchCancelling;
if (cache != null)
{
cache(this, EventArgs.Empty);
}
}
#endregion
#region Private enums
private enum BatchState
{
Initial,
Executing,
Executed,
ProcessingResults,
Cancelling,
}
#endregion
#region Private fields
// correspond to public properties
private bool isSuppressProviderMessageHeaders;
private bool isResultExpected = false;
private string sqlText = string.Empty;
private int execTimeout = 30;
private int scriptTrackingId = 0;
private bool isScriptExecutionTracked = false;
private const int ChangeDatabase = 0x1645;
//command that will be used for execution
private IDbCommand command = null;
//current object state
private BatchState state = BatchState.Initial;
//script text to be executed
private TextSpan textSpan;
//index of the batch in collection of batches
private int index = 0;
private int expectedExecutionCount = 1;
private long totalAffectedRows = 0;
private bool hasErrors;
// Expected showplan if any
private ShowPlanType expectedShowPlan;
#endregion
#region Constructors
/// <summary>
/// Default constructor
/// </summary>
public Batch()
{
// nothing
}
/// <summary>
/// Creates and initializes a batch object
/// </summary>
/// <param name="isResultExpected">Whether it is one of "set [something] on/off" type of command,
/// that doesn't return any results from the server
/// </param>
/// <param name="sqlText">Text of the batch</param>
/// <param name="execTimeout">Timeout for the batch execution. 0 means no limit </param>
public Batch(string sqlText, bool isResultExpected, int execTimeout)
{
this.isResultExpected = isResultExpected;
this.sqlText = sqlText;
this.execTimeout = execTimeout;
}
#endregion
#region Public properties
/// <summary>
/// Is the Batch's text valid?
/// </summary>
public bool HasValidText
{
get
{
return !string.IsNullOrEmpty(sqlText);
}
}
/// <summary>
/// SQL text that to be executed in the Batch
/// </summary>
public string Text
{
get
{
return sqlText;
}
set
{
sqlText = value;
}
}
/// <summary>
/// Determines whether batch execution returns any results
/// </summary>
public bool IsResultsExpected
{
get
{
return isResultExpected;
}
set
{
isResultExpected = value;
}
}
/// <summary>
/// Determines the execution timeout for the batch
/// </summary>
public int ExecutionTimeout
{
get
{
return execTimeout;
}
set
{
execTimeout = value;
}
}
/// <summary>
/// Determines the textspan to wich the batch belongs to
/// </summary>
public TextSpan TextSpan
{
get
{
return textSpan;
}
set
{
textSpan = value;
}
}
/// <summary>
/// Determines the batch index in the collection of batches being executed
/// </summary>
public int BatchIndex
{
get
{
return index;
}
set
{
index = value;
}
}
/// <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
/// in the UI.
/// </summary>
/// <remarks>
/// It can be used only after the execution of the batch is finished
/// </remarks>
public long RowsAffected
{
get
{
return totalAffectedRows;
}
}
/// <summary>
/// Determines if the error.Source should be used when messages are written
/// </summary>
public bool IsSuppressProviderMessageHeaders
{
get
{
return isSuppressProviderMessageHeaders;
}
set
{
isSuppressProviderMessageHeaders = value;
}
}
/// <summary>
/// Gets or sets the id of the script we are tracking
/// </summary>
public int ScriptTrackingId
{
get
{
return scriptTrackingId;
}
set
{
scriptTrackingId = value;
}
}
#endregion
#region Public events
/// <summary>
/// fired when there is an error message from the server
/// </summary>
public event EventHandler<BatchErrorEventArgs> BatchError = null;
/// <summary>
/// fired when there is a message from the server
/// </summary>
public event EventHandler<BatchMessageEventArgs> BatchMessage = null;
/// <summary>
/// fired when there is a new result set available. It is guarnteed
/// to be fired from the same thread that called Execute method
/// </summary>
public event EventHandler<BatchResultSetEventArgs> BatchResultSetProcessing = null;
/// <summary>
/// fired when the batch recieved cancel request BEFORE it
/// initiates cancel operation. Note that it is fired from a
/// different thread then the one used to kick off execution
/// </summary>
public event EventHandler<EventArgs> BatchCancelling = null;
/// <summary>
/// fired when we've done absolutely all actions for the current result set
/// </summary>
public event EventHandler<EventArgs> BatchResultSetFinished = null;
#endregion
#region Public methods
/// <summary>
/// Resets the object to its initial state
/// </summary>
public void Reset()
{
lock (this)
{
state = BatchState.Initial;
command = null;
textSpan = new TextSpan();
totalAffectedRows = 0;
hasErrors = false;
expectedShowPlan = ShowPlanType.None;
isSuppressProviderMessageHeaders = false;
scriptTrackingId = 0;
isScriptExecutionTracked = false;
}
}
/// <summary>
/// Executes the batch
/// </summary>
/// <param name="connection">Connection to use</param>
/// <param name="expectedShowPlan">ShowPlan type to be used</param>
/// <returns>result of execution</returns>
/// <remarks>
/// It does not return until execution is finished
/// We may have received a Cancel request by the time this function is called
/// </remarks>
public ScriptExecutionResult Execute(SqlConnection connection, ShowPlanType expectedShowPlan)
{
// FUTURE CLEANUP: Remove in favor of general signature (IDbConnection) - #920978
return Execute((IDbConnection)connection, expectedShowPlan);
}
/// <summary>
/// Executes the batch
/// </summary>
/// <param name="connection">Connection to use</param>
/// <param name="expectedShowPlan">ShowPlan type to be used</param>
/// <returns>result of execution</returns>
/// <remarks>
/// It does not return until execution is finished
/// We may have received a Cancel request by the time this function is called
/// </remarks>
public ScriptExecutionResult Execute(IDbConnection connection, ShowPlanType expectedShowPlan)
{
Validate.IsNotNull(nameof(connection), connection);
//makes sure that the batch is not in use
lock (this)
{
Debug.Assert(command == null, "SQLCommand is NOT null");
if (command != null)
{
command = null;
}
}
this.expectedShowPlan = expectedShowPlan;
return DoBatchExecutionImpl(connection, sqlText);
}
/// <summary>
/// Cancels the batch
/// </summary>
/// <remarks>
/// When batch is actually cancelled, Execute() will return with the appropiate status
/// </remarks>
public void Cancel()
{
lock (this)
{
if (state != BatchState.Cancelling)
{
state = BatchState.Cancelling;
RaiseCancelling();
if (command != null)
{
try
{
command.Cancel();
Debug.WriteLine("Batch.Cancel: command.Cancel completed");
}
catch (SqlException)
{
// eat it
}
catch (RetryLimitExceededException)
{
// eat it
}
}
}
}
}
#endregion
#region Protected methods
/// <summary>
/// Fires an error message event
/// </summary>
/// <param name="ex">Exception caught</param>
/// <remarks>
/// Non-SQL exception
/// </remarks>
protected void HandleExceptionMessage(Exception ex)
{
BatchErrorEventArgs args = new BatchErrorEventArgs(string.Format(CultureInfo.CurrentCulture, SR.EE_BatchError_Exception, ex.Message), ex);
RaiseBatchError(args);
}
/// <summary>
/// Fires a message event
/// </summary>
/// <param name="errors">SqlClient errors collection</param>
/// <remarks>
/// Sql specific messages.
/// </remarks>
protected void HandleSqlMessages(SqlErrorCollection errors)
{
foreach (SqlError error in errors)
{
if (error.Number == ChangeDatabase)
{
continue;
}
string detailedMessage = FormatSqlErrorMessage(error);
if (error.Class > 10)
{
// expose this event as error
Debug.Assert(detailedMessage.Length != 0);
RaiseBatchError(detailedMessage, error, textSpan);
//at least one error message has been used
hasErrors = true;
}
else
{
RaiseBatchMessage(detailedMessage, error.Message, error);
}
}
}
/// <summary>
/// method that will be passed as delegate to SqlConnection.InfoMessage
/// </summary>
protected void OnSqlInfoMessageCallback(object sender, SqlInfoMessageEventArgs e)
{
HandleSqlMessages(e.Errors);
}
/// <summary>
/// Delegete for SqlCommand.RecordsAffected
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>
/// This is exposed as a regular message
/// </remarks>
protected void OnStatementExecutionFinished(object sender, StatementCompletedEventArgs e)
{
string message = string.Format(CultureInfo.CurrentCulture, SR.EE_BatchExecutionInfo_RowsAffected,
e.RecordCount.ToString(System.Globalization.CultureInfo.InvariantCulture));
RaiseBatchMessage(message, message, null);
}
/// <summary>
/// Called on a new ResultSet on the data reader
/// </summary>
/// <param name="dataReader">True if result set consumed, false on a Cancel request</param>
/// <returns></returns>
/// <remarks>
/// The GridStorageResultSet created is owned by the batch consumer. It's only created here.
/// Additionally, when BatchResultSet event handler is called, it won't return until
/// all data is prcessed or the data being processed is terminated (i.e. cancel or error)
/// </remarks>
protected ScriptExecutionResult ProcessResultSet(IDataReader dataReader)
{
if (dataReader == null)
{
throw new ArgumentNullException();
}
Debug.WriteLine("ProcessResultSet: result set has been created");
//initialize result variable that will be set by batch consumer
ScriptExecutionResult scriptExecutionResult = ScriptExecutionResult.Success;
RaiseBatchResultSetProcessing(dataReader, expectedShowPlan);
if (state != BatchState.Cancelling)
{
return scriptExecutionResult;
}
else
{
return ScriptExecutionResult.Cancel;
}
}
// FUTURE CLEANUP: Remove in favor of general signature (IDbConnection) - #920978
protected ScriptExecutionResult DoBatchExecution(SqlConnection connection, string script)
{
return DoBatchExecutionImpl(connection, script);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities"), SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
[SuppressMessage("Microsoft.Usage", "CA2219:DoNotRaiseExceptionsInExceptionClauses")]
private ScriptExecutionResult DoBatchExecutionImpl(IDbConnection connection, string script)
{
Validate.IsNotNull(nameof(connection), connection);
lock (this)
{
if (state == BatchState.Cancelling)
{
state = BatchState.Initial;
return ScriptExecutionResult.Cancel;
}
}
ScriptExecutionResult result = ScriptExecutionResult.Success;
// SqlClient event handlers setup
SqlInfoMessageEventHandler messageHandler = new SqlInfoMessageEventHandler(OnSqlInfoMessageCallback);
StatementCompletedEventHandler statementCompletedHandler = null;
DbConnectionWrapper connectionWrapper = new DbConnectionWrapper(connection);
connectionWrapper.InfoMessage += messageHandler;
IDbCommand command = connection.CreateCommand();
command.CommandText = script;
command.CommandTimeout = execTimeout;
DbCommandWrapper commandWrapper = null;
if (isScriptExecutionTracked && DbCommandWrapper.IsSupportedCommand(command))
{
statementCompletedHandler = new StatementCompletedEventHandler(OnStatementExecutionFinished);
commandWrapper = new DbCommandWrapper(command);
commandWrapper.StatementCompleted += statementCompletedHandler;
}
lock (this)
{
state = BatchState.Executing;
this.command = command;
command = null;
}
try
{
result = this.ExecuteCommand();
}
catch (OutOfMemoryException)
{
throw;
}
catch (SqlException sqlEx)
{
result = HandleSqlException(sqlEx);
}
catch (Exception ex)
{
result = ScriptExecutionResult.Failure;
HandleExceptionMessage(ex);
}
finally
{
if (messageHandler == null)
{
Logger.Write(TraceEventType.Error, "Expected handler to be declared");
}
if (null != connectionWrapper)
{
connectionWrapper.InfoMessage -= messageHandler;
}
if (commandWrapper != null)
{
if (statementCompletedHandler == null)
{
Logger.Write(TraceEventType.Error, "Expect handler to be declared if we have a command wrapper");
}
commandWrapper.StatementCompleted -= statementCompletedHandler;
}
lock (this)
{
state = BatchState.Initial;
if (command != null)
{
command.Dispose();
command = null;
}
}
}
return result;
}
private ScriptExecutionResult ExecuteCommand()
{
if (command == null)
{
throw new ArgumentNullException("command");
}
return this.ExecuteUnTrackedCommand();
}
private ScriptExecutionResult ExecuteUnTrackedCommand()
{
IDataReader reader = null;
if (!isResultExpected)
{
command.ExecuteNonQuery();
}
else
{
reader = command.ExecuteReader(CommandBehavior.SequentialAccess);
}
return this.CheckStateAndRead(reader);
}
private ScriptExecutionResult CheckStateAndRead(IDataReader reader = null)
{
ScriptExecutionResult result = ScriptExecutionResult.Success;
if (!isResultExpected)
{
lock (this)
{
if (state == BatchState.Cancelling)
{
result = ScriptExecutionResult.Cancel;
}
else
{
result = ScriptExecutionResult.Success;
state = BatchState.Executed;
}
}
}
else
{
lock (this)
{
if (state == BatchState.Cancelling)
{
result = ScriptExecutionResult.Cancel;
}
else
{
state = BatchState.ProcessingResults;
}
}
if (result != ScriptExecutionResult.Cancel)
{
ScriptExecutionResult batchExecutionResult = ScriptExecutionResult.Success;
if (reader != null)
{
bool hasNextResult = false;
do
{
// if there were no results coming from the server, then the FieldCount is 0
if (reader.FieldCount <= 0)
{
hasNextResult = reader.NextResult();
continue;
}
batchExecutionResult = ProcessResultSet(reader);
if (batchExecutionResult != ScriptExecutionResult.Success)
{
result = batchExecutionResult;
break;
}
RaiseBatchResultSetFinished();
hasNextResult = reader.NextResult();
} while (hasNextResult);
}
if (hasErrors)
{
Debug.WriteLine("DoBatchExecution: successfull processed result set, but there were errors shown to the user");
result = ScriptExecutionResult.Failure;
}
if (result != ScriptExecutionResult.Cancel)
{
lock (this)
{
state = BatchState.Executed;
}
}
}
}
if (reader != null)
{
try
{
// reader.Close() doesn't actually close the reader
// so explicitly dispose the reader
reader.Dispose();
reader = null;
}
catch (OutOfMemoryException)
{
throw;
}
catch (SqlException)
{
// nothing
}
}
return result;
}
#endregion
}
}

View File

@@ -0,0 +1,75 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
///<summary>
/// Class to get text from the BatchParser and convert them into batches
///</summary>
public class BatchDefinition
{
/// <summary>
/// Constructor method for a BatchDefinition
/// </summary>
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>
/// Get starting line of the BatchDefinition
/// </summary>
public int StartLine
{
get; private set;
}
/// <summary>
/// Get ending line of the BatchDefinition
/// </summary>
public int EndLine
{
get; private set;
}
/// <summary>
/// Get starting column of the BatchDefinition
/// </summary>
public int StartColumn
{
get; private set;
}
/// <summary>
/// Get ending column of the BatchDefinition
/// </summary>
public int EndColumn
{
get; private set;
}
/// <summary>
/// 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

@@ -0,0 +1,142 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Data.SqlClient;
using Microsoft.SqlTools.ManagedBatchParser;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Error totalAffectedRows for a Batch
/// </summary>
public class BatchErrorEventArgs : EventArgs
{
#region Private Fields
private string message = string.Empty;
private string description = string.Empty;
private int line = -1;
private TextSpan textSpan;
private Exception exception;
private SqlError error;
#endregion
#region Constructors / Destructor
/// <summary>
/// Default constructor
/// </summary>
private BatchErrorEventArgs()
{
}
/// <summary>
/// Constructor with message and no description
/// </summary>
internal BatchErrorEventArgs(string message)
: this(message, null)
{
}
/// <summary>
/// Constructor with exception and no description
/// </summary>
internal BatchErrorEventArgs(string message, Exception ex)
: this(message, string.Empty, ex)
{
}
/// <summary>
/// Constructor with message and description
/// </summary>
internal BatchErrorEventArgs(string message, string description, Exception ex)
: this(message, description, -1, new TextSpan(), ex)
{
}
internal BatchErrorEventArgs(string message, SqlError error, TextSpan textSpan, Exception ex)
{
string desc = error != null ? error.Message : null;
if (error.Number == 7202)
{
desc += " " + Environment.NewLine + SR.TroubleshootingAssistanceMessage;
}
int lineNumber = error != null ? error.LineNumber : -1;
Init(message, desc, lineNumber, textSpan, ex);
this.error = error;
}
/// <summary>
/// Constructor with message, description, textspan and line number
/// </summary>
internal BatchErrorEventArgs(string message, string description, int line, TextSpan textSpan, Exception ex)
{
Init(message, description, line, textSpan, ex);
}
private void Init(string message, string description, int line, TextSpan textSpan, Exception ex)
{
this.message = message;
this.description = description;
this.line = line;
this.textSpan = textSpan;
exception = ex;
}
#endregion
#region Public properties
public string Message
{
get
{
return message;
}
}
public string Description
{
get
{
return description;
}
}
public int Line
{
get
{
return line;
}
}
public TextSpan TextSpan
{
get
{
return textSpan;
}
}
public Exception Exception
{
get
{
return exception;
}
}
public SqlError Error
{
get { return error; }
}
#endregion
}
}

View File

@@ -0,0 +1,58 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Event args for notification about non-error message
/// </summary>
public class BatchMessageEventArgs : EventArgs
{
private readonly string message = string.Empty;
private readonly string detailedMessage = string.Empty;
private readonly SqlError error;
private BatchMessageEventArgs()
{
}
internal BatchMessageEventArgs(string msg)
: this(string.Empty, msg)
{
}
internal BatchMessageEventArgs(string detailedMsg, string msg) : this(detailedMsg, msg, null)
{
}
internal BatchMessageEventArgs(string detailedMsg, string msg, SqlError error)
{
message = msg;
detailedMessage = detailedMsg;
this.error = error;
}
public string Message
{
get
{
return message;
}
}
public string DetailedMessage
{
get
{
return detailedMessage;
}
}
public SqlError Error { get { return error; } }
}
}

View File

@@ -0,0 +1,161 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Class that parses queries into batches
/// </summary>
public class BatchParser :
ICommandHandler,
IVariableResolver
{
#region Private fields
protected ScriptMessageDelegate scriptMessageDelegate;
protected ScriptErrorDelegate scriptErrorDelegate;
protected ExecuteDelegate executeDelegate;
protected HaltParserDelegate haltParserDelegate;
private int startingLine = 0;
protected bool variableSubstitutionDisabled = false;
#endregion
#region Public delegates
public delegate void HaltParserDelegate();
public delegate void ScriptMessageDelegate(string message);
public delegate void ScriptErrorDelegate(string message, ScriptMessageType messageType);
public delegate bool ExecuteDelegate(string batchScript, int num, int lineNumber);
#endregion
#region Constructors / Destructor
public BatchParser()
{
}
#endregion
#region Public properties
public ScriptMessageDelegate Message
{
get { return scriptMessageDelegate; }
set { scriptMessageDelegate = value; }
}
public ScriptErrorDelegate ErrorMessage
{
get { return scriptErrorDelegate; }
set { scriptErrorDelegate = value; }
}
public ExecuteDelegate Execute
{
get { return executeDelegate; }
set { executeDelegate = value; }
}
public HaltParserDelegate HaltParser
{
get { return haltParserDelegate; }
set { haltParserDelegate = value; }
}
public int StartingLine
{
get { return startingLine; }
set { startingLine = value; }
}
#endregion
#region ICommandHandler Members
/// <summary>
/// Take approptiate action on the parsed batches
/// </summary>
public BatchParserAction Go(TextBlock batch, int repeatCount)
{
string str;
LineInfo lineInfo;
batch.GetText(!variableSubstitutionDisabled, out str, out lineInfo);
bool executeResult = false;
if (executeDelegate != null)
{
executeResult = executeDelegate(str, repeatCount, lineInfo.GetStreamPositionForOffset(0).Line + startingLine - 1);
}
return executeResult ? BatchParserAction.Continue : BatchParserAction.Abort;
}
#endregion
#region Protected methods
/// <summary>
/// Called when the script parsing has errors/warnings
/// </summary>
/// <param name="message"></param>
/// <param name="messageType"></param>
protected void RaiseScriptError(string message, ScriptMessageType messageType)
{
if (scriptErrorDelegate != null)
{
scriptErrorDelegate(message, messageType);
}
}
/// <summary>
/// Called on parsing info message
/// </summary>
/// <param name="message"></param>
/// <param name="messageType"></param>
protected void RaiseScriptMessage(string message)
{
if (scriptMessageDelegate != null)
{
scriptMessageDelegate(message);
}
}
/// <summary>
/// Called on parsing info message
/// </summary>
/// <param name="message"></param>
/// <param name="messageType"></param>
protected void RaiseHaltParser()
{
if (haltParserDelegate != null)
{
haltParserDelegate();
}
}
#endregion
public virtual BatchParserAction OnError(Token token, OnErrorAction action)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
public virtual BatchParserAction Include(TextBlock filename, out System.IO.TextReader stream, out string newFilename)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
public virtual string GetVariable(PositionStruct pos, string name)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
public virtual void SetVariable(PositionStruct pos, string name, string value)
{
throw new NotImplementedException("The method or operation is not implemented.");
}
public void DisableVariableSubstitution()
{
variableSubstitutionDisabled = true;
}
}
}

View File

@@ -0,0 +1,33 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Class associated with batch parser execution errors
/// </summary>
public class BatchParserExecutionErrorEventArgs : BatchErrorEventArgs
{
private readonly ScriptMessageType messageType;
/// <summary>
/// Constructor method for BatchParserExecutionErrorEventArgs class
/// </summary>
public BatchParserExecutionErrorEventArgs(string errorLine, string message, ScriptMessageType messageType)
: base(errorLine, message, null)
{
this.messageType = messageType;
}
public ScriptMessageType MessageType
{
get
{
return messageType;
}
}
}
}

View File

@@ -0,0 +1,48 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Class associated with batch parser execution finished event
/// </summary>
public class BatchParserExecutionFinishedEventArgs : EventArgs
{
private readonly Batch batch = null;
private readonly ScriptExecutionResult result;
private BatchParserExecutionFinishedEventArgs()
{
}
/// <summary>
/// Constructor method for the class
/// </summary>
public BatchParserExecutionFinishedEventArgs(ScriptExecutionResult batchResult, Batch batch)
{
this.batch = batch;
result = batchResult;
}
public Batch Batch
{
get
{
return batch;
}
}
public ScriptExecutionResult ExecutionResult
{
get
{
return result;
}
}
}
}

View File

@@ -0,0 +1,50 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Class associated with batch parser execution start event
/// </summary>
public class BatchParserExecutionStartEventArgs : EventArgs
{
private readonly Batch batch = null;
private readonly TextSpan textSpan;
private BatchParserExecutionStartEventArgs()
{
}
/// <summary>
/// Contructor method for the class
/// </summary>
public BatchParserExecutionStartEventArgs(TextSpan textSpan, Batch batch)
{
this.batch = batch;
this.textSpan = textSpan;
}
public Batch Batch
{
get
{
return batch;
}
}
public TextSpan TextSpan
{
get
{
return textSpan;
}
}
}
}

View File

@@ -0,0 +1,134 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.IO;
using Microsoft.SqlTools.ManagedBatchParser;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Class for handling SQL CMD by Batch Parser
/// </summary>
public class BatchParserSqlCmd : BatchParser
{
/// <summary>
/// The internal variables that can be used in SqlCommand substitution.
/// These variables take precedence over environment variables.
/// </summary>
private Dictionary<string, string> internalVariables = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
private ConnectionChangedDelegate connectionChangedDelegate;
private ErrorActionChangedDelegate errorActionChangedDelegate;
public delegate void ConnectionChangedDelegate(SqlConnectionStringBuilder connectionstringBuilder);
public delegate void ErrorActionChangedDelegate(OnErrorAction ea);
/// <summary>
/// Constructor taking a Parser instance
/// </summary>
/// <param name="parser"></param>
public BatchParserSqlCmd()
: base()
{
// nothing
}
public ConnectionChangedDelegate ConnectionChanged
{
get { return connectionChangedDelegate; }
set { connectionChangedDelegate = value; }
}
public ErrorActionChangedDelegate ErrorActionChanged
{
get { return errorActionChangedDelegate; }
set { errorActionChangedDelegate = value; }
}
/// <summary>
/// Looks for any environment variable or internal variable.
/// </summary>
public override string GetVariable(PositionStruct pos, string name)
{
if (variableSubstitutionDisabled)
{
return null;
}
string value;
// Internally defined variables have higher precedence over environment variables.
if (!internalVariables.TryGetValue(name, out value))
{
value = Environment.GetEnvironmentVariables()[name] as string;
}
if (value == null)
{
RaiseScriptError(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionError_VariableNotFound, name), ScriptMessageType.FatalError);
RaiseHaltParser();
// TODO: Halt the parser, should get/set variable have ParserAction.Abort/Continue (like original?)
}
return value;
}
/// <summary>
/// Set environment or internal variable
/// </summary>
public override void SetVariable(PositionStruct pos, string name, string value)
{
if (variableSubstitutionDisabled)
{
return;
}
if (value == null)
{
if (internalVariables.ContainsKey(name))
{
internalVariables.Remove(name);
}
}
else
{
internalVariables[name] = value;
}
}
public Dictionary<string, string> InternalVariables
{
get { return internalVariables; }
set { internalVariables = value; }
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "ppIBatchSource")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fileName")]
public override BatchParserAction Include(TextBlock filename, out TextReader stream, out string newFilename)
{
stream = null;
newFilename = null;
RaiseScriptError(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionError_CommandNotSupported, "Include"), ScriptMessageType.Error);
return BatchParserAction.Abort;
}
/// <summary>
/// Method to deal with errors
/// </summary>
public override BatchParserAction OnError(Token token, OnErrorAction ea)
{
if (errorActionChangedDelegate != null)
{
errorActionChangedDelegate(ea);
}
return BatchParserAction.Continue;
}
}
}

View File

@@ -0,0 +1,52 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Data;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
/// <summary>
/// Class associated with setting batch results
/// </summary>
public class BatchResultSetEventArgs : EventArgs
{
private readonly IDataReader dataReader = null;
private readonly ShowPlanType expectedShowPlan = ShowPlanType.None;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="dataReader"></param>
internal BatchResultSetEventArgs(IDataReader dataReader, ShowPlanType expectedShowPlan)
{
this.dataReader = dataReader;
this.expectedShowPlan = expectedShowPlan;
}
/// <summary>
/// Data reader associated with the result set
/// </summary>
public IDataReader DataReader
{
get
{
return dataReader;
}
}
/// <summary>
/// Show Plan to be expected if any during the execution
/// </summary>
public ShowPlanType ExpectedShowPlan
{
get
{
return expectedShowPlan;
}
}
}
}

View File

@@ -0,0 +1,277 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Specialized;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
public class ExecutionEngineConditions
{
#region Private fields
private static class Consts
{
public static string On = "ON";
public static string Off = "OFF";
public static string ParseOnly = "SET PARSEONLY {0}";
public static string NoExec = "SET NOEXEC {0}";
public static string StatisticsIO = "SET STATISTICS IO {0}";
public static string StatisticsTime = "SET STATISTICS TIME {0}";
public static string ShowPlanXml = "SET SHOWPLAN_XML {0}";
public static string ShowPlanAll = "SET SHOWPLAN_ALL {0}";
public static string ShowPlanText = "SET SHOWPLAN_TEXT {0}";
public static string StatisticsXml = "SET STATISTICS XML {0}";
public static string StatisticsProfile = "SET STATISTICS PROFILE {0}";
public static string BeginTrans = "BEGIN TRAN";
public static string CommitTrans = "COMMIT TRAN";
public static string Rollback = "ROLLBACK";
public static string BatchSeparator = "GO";
public static string Reset = "SET NOEXEC, FMTONLY OFF, PARSEONLY, SET SHOWPLAN_ALL, SET SHOWPLAN_TEXT";
}
private static readonly int stateParseOnly = BitVector32.CreateMask();
private static readonly int stateTransactionWrapped = BitVector32.CreateMask(stateParseOnly);
private static readonly int stateHaltOnError = BitVector32.CreateMask(stateTransactionWrapped);
private static readonly int stateEstimatedShowPlan = BitVector32.CreateMask(stateHaltOnError);
private static readonly int stateActualShowPlan = BitVector32.CreateMask(stateEstimatedShowPlan);
private static readonly int stateSuppressProviderMessageHeaders = BitVector32.CreateMask(stateActualShowPlan);
private static readonly int stateNoExec = BitVector32.CreateMask(stateSuppressProviderMessageHeaders);
private static readonly int stateStatisticsIO = BitVector32.CreateMask(stateNoExec);
private static readonly int stateShowPlanText = BitVector32.CreateMask(stateStatisticsIO);
private static readonly int stateStatisticsTime = BitVector32.CreateMask(stateShowPlanText);
private static readonly int stateSqlCmd = BitVector32.CreateMask(stateStatisticsTime);
private static readonly int stateScriptExecutionTracked = BitVector32.CreateMask(stateSqlCmd);
private BitVector32 state = new BitVector32();
private string batchSeparator = Consts.BatchSeparator;
#endregion
#region Constructors / Destructor
/// <summary>
/// Default constructor
/// </summary>
public ExecutionEngineConditions()
{
// nothing
}
/// <summary>
/// Overloaded constructor taking another ExecutionEngineCondition object as a reference
/// </summary>
public ExecutionEngineConditions(ExecutionEngineConditions condition)
{
state = condition.state;
batchSeparator = condition.batchSeparator;
}
#endregion
#region Statement strings
public static string ShowPlanXmlStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.ShowPlanXml, (isOn ? Consts.On : Consts.Off));
}
public static string ShowPlanAllStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.ShowPlanAll, (isOn ? Consts.On : Consts.Off));
}
public static string ShowPlanTextStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.ShowPlanText, (isOn ? Consts.On : Consts.Off));
}
public static string StatisticsXmlStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.StatisticsXml, (isOn ? Consts.On : Consts.Off));
}
public static string StatisticsProfileStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.StatisticsProfile, (isOn ? Consts.On : Consts.Off));
}
public static string ParseOnlyStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.ParseOnly, (isOn ? Consts.On : Consts.Off));
}
public static string NoExecStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.NoExec, (isOn ? Consts.On : Consts.Off));
}
public static string StatisticsIOStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.StatisticsIO, (isOn ? Consts.On : Consts.Off));
}
public static string StatisticsTimeStatement(bool isOn)
{
return string.Format(System.Globalization.CultureInfo.InvariantCulture, Consts.StatisticsTime, (isOn ? Consts.On : Consts.Off));
}
public static string BeginTransactionStatement
{
get { return Consts.BeginTrans; }
}
public static string CommitTransactionStatement
{
get { return Consts.CommitTrans; }
}
public static string RollbackTransactionStatement
{
get { return Consts.Rollback; }
}
public static string BatchSeparatorStatement
{
get { return Consts.BatchSeparator; }
}
public static string ResetStatement
{
get { return Consts.Reset; }
}
#endregion
#region Public properties
/// <summary>
/// Checks the syntax of each Transact-SQL statement and returns any error messages
/// without compiling or executing the statement.
/// </summary>
public bool IsParseOnly
{
get { return state[stateParseOnly]; }
set { state[stateParseOnly] = value; }
}
/// <summary>
/// Script is wrapped withint BEGIN TRAN/COMMIT-ROLLBACK
/// </summary>
public bool IsTransactionWrapped
{
get { return state[stateTransactionWrapped]; }
set { state[stateTransactionWrapped] = value; }
}
/// <summary>
/// Gets or a set a value indicating whether script execution is tracked
/// </summary>
public bool IsScriptExecutionTracked
{
get { return state[stateScriptExecutionTracked]; }
set { state[stateScriptExecutionTracked] = value; }
}
/// <summary>
/// Halts the execution if an error is found
/// </summary>
public bool IsHaltOnError
{
get { return state[stateHaltOnError]; }
set { state[stateHaltOnError] = value; }
}
/// <summary>
/// Use estimated show plan
/// </summary>
public bool IsEstimatedShowPlan
{
get { return state[stateEstimatedShowPlan]; }
set { state[stateEstimatedShowPlan] = value; }
}
/// <summary>
/// Use actual show plan
/// </summary>
public bool IsActualShowPlan
{
get { return state[stateActualShowPlan]; }
set { state[stateActualShowPlan] = value; }
}
/// <summary>
/// Use Source information on messages shown to the user
/// </summary>
public bool IsSuppressProviderMessageHeaders
{
get { return state[stateSuppressProviderMessageHeaders]; }
set { state[stateSuppressProviderMessageHeaders] = value; }
}
/// <summary>
/// SET NO EXEC {on/off}
/// </summary>
public bool IsNoExec
{
get { return state[stateNoExec]; }
set { state[stateNoExec] = value; }
}
/// <summary>
/// SET STATISTICS IO {on/off}
/// </summary>
public bool IsStatisticsIO
{
get { return state[stateStatisticsIO]; }
set { state[stateStatisticsIO] = value; }
}
/// <summary>
/// SET SHOWPLAN_TEXT {on/off}
/// </summary>
public bool IsShowPlanText
{
get { return state[stateShowPlanText]; }
set { state[stateShowPlanText] = value; }
}
/// <summary>
/// SET STATISTICS IO {on/off}
/// </summary>
public bool IsStatisticsTime
{
get { return state[stateStatisticsTime]; }
set { state[stateStatisticsTime] = value; }
}
/// <summary>
/// SqlCmd support
/// </summary>
public bool IsSqlCmd
{
get { return state[stateSqlCmd]; }
set { state[stateSqlCmd] = value; }
}
/// <summary>
/// Batch separator statement
/// </summary>
public string BatchSeparator
{
get
{
return batchSeparator;
}
set
{
Validate.IsNotNullOrEmptyString(nameof(value), value);
batchSeparator = value;
}
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
internal enum ExecutionState
{
Initial,
Executing,
ExecutingBatch,
Cancelling,
Discarded
}
}

View File

@@ -0,0 +1,40 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
public interface IBatchEventsHandler
{
/// <summary>
/// fired when there is an error message from the server
/// </summary>
void OnBatchError(object sender, BatchErrorEventArgs args);
/// <summary>
/// fired when there is a message from the server
/// </summary>
void OnBatchMessage(object sender, BatchMessageEventArgs args);
/// <summary>
/// fired when there is a new result set available. It is guarnteed
/// to be fired from the same thread that called Execute method
/// </summary>
void OnBatchResultSetProcessing(object sender, BatchResultSetEventArgs args);
/// <summary>
/// fired when we've done absolutely all actions for the current result set
/// </summary>
void OnBatchResultSetFinished(object sender, EventArgs args);
/// <summary>
/// fired when the batch recieved cancel request BEFORE it
/// initiates cancel operation. Note that it is fired from a
/// different thread then the one used to kick off execution
/// </summary>
void OnBatchCancelling(object sender, EventArgs args);
}
}

View File

@@ -0,0 +1,143 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
public class ScriptExecutionArgs : EventArgs
{
private IDbConnection connection;
private IBatchEventsHandler batchEventHandlers;
private int startingLine;
private Dictionary<string, string> cmdVariables;
#region Constructors / Destructor
/// <summary>
/// Constructor method for ScriptExecutionArgs
/// </summary>
public ScriptExecutionArgs(
string script,
SqlConnection connection,
int timeOut,
ExecutionEngineConditions conditions,
IBatchEventsHandler batchEventHandlers)
: this (script, (IDbConnection)connection, timeOut, conditions, batchEventHandlers)
{
// nothing
}
/// <summary>
/// Constructor method for ScriptExecutionArgs
/// </summary>
public ScriptExecutionArgs(
string script,
SqlConnection connection,
int timeOut,
ExecutionEngineConditions conditions,
IBatchEventsHandler batchEventHandlers,
int startingLine,
IDictionary<string,string> variables)
: this(script, (IDbConnection) connection, timeOut, conditions, batchEventHandlers, startingLine, variables)
{
// nothing
}
/// <summary>
/// Constructor method for ScriptExecutionArgs
/// </summary>
public ScriptExecutionArgs(
string script,
IDbConnection connection,
int timeOut,
ExecutionEngineConditions conditions,
IBatchEventsHandler batchEventHandlers)
: this(script, connection, timeOut, conditions, batchEventHandlers, 0, null)
{
// nothing
}
/// <summary>
/// Constructor method for ScriptExecutionArgs
/// </summary>
public ScriptExecutionArgs(
string script,
IDbConnection connection,
int timeOut,
ExecutionEngineConditions conditions,
IBatchEventsHandler batchEventHandlers,
int startingLine,
IDictionary<string, string> variables)
{
Script = script;
this.connection = connection;
TimeOut = timeOut;
Conditions = conditions;
this.batchEventHandlers = batchEventHandlers;
this.startingLine = startingLine;
if (variables != null)
{
foreach (var variable in variables)
{
Variables[variable.Key] = variable.Value;
}
}
}
#endregion
#region Public properties
public string Script { get; set; }
// FUTURE CLEANUP: Remove in favor of general signature (IDbConnection) - #920978
public SqlConnection Connection
{
get { return connection as SqlConnection; }
set { connection = value as SqlConnection; }
}
public IDbConnection ReliableConnection
{
get { return connection; }
set { connection = value; }
}
public int TimeOut { get; set; }
internal ExecutionEngineConditions Conditions { get; set; }
internal IBatchEventsHandler BatchEventHandlers
{
get { return batchEventHandlers; }
set { batchEventHandlers = value; }
}
internal int StartingLine
{
get { return startingLine; }
set { startingLine = value; }
}
internal Dictionary<string, string> Variables
{
get
{
if (cmdVariables == null)
{
cmdVariables = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
}
return cmdVariables;
}
}
#endregion
}
}

View File

@@ -0,0 +1,29 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
public class ScriptExecutionFinishedEventArgs : EventArgs
{
internal ScriptExecutionFinishedEventArgs(ScriptExecutionResult result)
{
ExecutionResult = result;
}
public ScriptExecutionResult ExecutionResult
{
private set;
get;
}
public bool Disposing
{
get;
set;
}
}
}

View File

@@ -0,0 +1,20 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
[Flags]
public enum ScriptExecutionResult
{
Success = 0x1,
Failure = 0x2,
Cancel = 0x4,
Halted = 0x8,
All = 0x0F
}
}

View File

@@ -0,0 +1,14 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
public enum ScriptMessageType
{
FatalError,
Error,
Warning
}
}

View File

@@ -0,0 +1,20 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
[System.Flags]
public enum ShowPlanType
{
None = 0x0,
ActualExecutionShowPlan = 0x1,
ActualXmlShowPlan = 0x2,
EstimatedExecutionShowPlan = 0x4,
EstimatedXmlShowPlan = 0x8,
AllXmlShowPlan = ActualXmlShowPlan | EstimatedXmlShowPlan,
AllExecutionShowPlan = ActualExecutionShowPlan | EstimatedExecutionShowPlan,
AllShowPlan = AllExecutionShowPlan | AllXmlShowPlan
}
}

View File

@@ -0,0 +1,15 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
{
public struct TextSpan
{
public int iEndIndex;
public int iEndLine;
public int iStartIndex;
public int iStartLine;
}
}