mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -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:
@@ -71,7 +71,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
endLine,
|
endLine,
|
||||||
startColumn + 1,
|
startColumn + 1,
|
||||||
endColumn + 1,
|
endColumn + 1,
|
||||||
batchInfos[0].executionCount
|
batchInfos[0].executionCount,
|
||||||
|
batchInfos[0].sqlCmdCommand
|
||||||
);
|
);
|
||||||
|
|
||||||
batchDefinitionList.Add(batchDef);
|
batchDefinitionList.Add(batchDef);
|
||||||
@@ -100,7 +101,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
endLine,
|
endLine,
|
||||||
startColumn + 1,
|
startColumn + 1,
|
||||||
endColumn + 1,
|
endColumn + 1,
|
||||||
batchInfos[index].executionCount
|
batchInfos[index].executionCount,
|
||||||
|
batchInfos[index].sqlCmdCommand
|
||||||
);
|
);
|
||||||
batchDefinitionList.Add(batch);
|
batchDefinitionList.Add(batch);
|
||||||
}
|
}
|
||||||
@@ -235,7 +237,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
endLine,
|
endLine,
|
||||||
startColumn + 1,
|
startColumn + 1,
|
||||||
endColumn + 1,
|
endColumn + 1,
|
||||||
batchInfo.executionCount
|
batchInfo.executionCount,
|
||||||
|
batchInfo.sqlCmdCommand
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +384,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the script info
|
// Add the script info
|
||||||
batchInfos.Add(new BatchInfo(args.Batch.TextSpan.iStartLine, args.Batch.TextSpan.iStartIndex, batchText, args.Batch.ExpectedExecutionCount));
|
batchInfos.Add(new BatchInfo(args.Batch.TextSpan.iStartLine, args.Batch.TextSpan.iStartIndex, batchText, args.SqlCmdCommand, args.Batch.ExpectedExecutionCount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (NotImplementedException)
|
catch (NotImplementedException)
|
||||||
@@ -474,17 +477,19 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
|
|
||||||
private class BatchInfo
|
private class BatchInfo
|
||||||
{
|
{
|
||||||
public BatchInfo(int startLine, int startColumn, string batchText, int repeatCount = 1)
|
public BatchInfo(int startLine, int startColumn, string batchText, SqlCmdCommand sqlCmdCommand, int repeatCount = 1)
|
||||||
{
|
{
|
||||||
this.startLine = startLine;
|
this.startLine = startLine;
|
||||||
this.startColumn = startColumn;
|
this.startColumn = startColumn;
|
||||||
this.executionCount = repeatCount;
|
this.executionCount = repeatCount;
|
||||||
this.batchText = batchText;
|
this.batchText = batchText;
|
||||||
|
this.sqlCmdCommand = sqlCmdCommand;
|
||||||
}
|
}
|
||||||
public int startLine;
|
public int startLine;
|
||||||
public int startColumn;
|
public int startColumn;
|
||||||
public int executionCount;
|
public int executionCount;
|
||||||
public string batchText;
|
public string batchText;
|
||||||
|
public SqlCmdCommand sqlCmdCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
using Microsoft.SqlServer.Management.Common;
|
||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
||||||
|
{
|
||||||
|
public class ConnectSqlCmdCommand : SqlCmdCommand
|
||||||
|
{
|
||||||
|
internal ConnectSqlCmdCommand(string server, string username, string password) : base(LexerTokenType.Connect)
|
||||||
|
{
|
||||||
|
Server = server;
|
||||||
|
UserName = username;
|
||||||
|
Password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Server { get; private set; }
|
||||||
|
public string UserName { get; private set; }
|
||||||
|
public string Password { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// attempts to establish connection with given params
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>returns the connection object is successful, throws otherwise</returns>
|
||||||
|
public DbConnection Connect()
|
||||||
|
{
|
||||||
|
//create SqlConnectionInfo object
|
||||||
|
SqlConnectionInfo connectionInfo = new SqlConnectionInfo();
|
||||||
|
if (Server != null && Server.Length > 0)
|
||||||
|
{
|
||||||
|
connectionInfo.ServerName = Server;
|
||||||
|
}
|
||||||
|
if (UserName != null && UserName.Length > 0)
|
||||||
|
{
|
||||||
|
connectionInfo.UseIntegratedSecurity = false;
|
||||||
|
connectionInfo.UserName = UserName;
|
||||||
|
connectionInfo.Password = Password;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connectionInfo.UseIntegratedSecurity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DbConnection dbConnection = AttemptToEstablishCurConnection(connectionInfo);
|
||||||
|
return dbConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// called when we need to establish new connection for batch executio as a
|
||||||
|
/// result of "connect" command processing
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ci"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private DbConnection AttemptToEstablishCurConnection(SqlConnectionInfo ci)
|
||||||
|
{
|
||||||
|
if (ci == null || ci.ServerName == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDbConnection conn = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string connString = ci.ConnectionString;
|
||||||
|
connString += ";Pooling=false"; //turn off connection pooling (this is done in other tools so following the same pattern)
|
||||||
|
|
||||||
|
conn = new SqlConnection(connString);
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
return conn as DbConnection;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to Change connection to {ci.ServerName}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor method for a BatchDefinition
|
/// Constructor method for a BatchDefinition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BatchDefinition(string batchText, int startLine, int endLine, int startColumn, int endColumn, int executionCount)
|
public BatchDefinition(string batchText, int startLine, int endLine, int startColumn, int endColumn, int executionCount, SqlCmdCommand command)
|
||||||
{
|
{
|
||||||
BatchText = batchText;
|
BatchText = batchText;
|
||||||
StartLine = startLine;
|
StartLine = startLine;
|
||||||
@@ -22,6 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
EndColumn = endColumn;
|
EndColumn = endColumn;
|
||||||
// set the batch execution count, with min value of 1
|
// set the batch execution count, with min value of 1
|
||||||
BatchExecutionCount = executionCount > 0 ? executionCount : 1;
|
BatchExecutionCount = executionCount > 0 ? executionCount : 1;
|
||||||
|
SqlCmdCommand = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,6 +65,11 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SqlCmdCommand SqlCmdCommand
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get number of times to execute this batch
|
/// Get number of times to execute this batch
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
public delegate void HaltParserDelegate();
|
public delegate void HaltParserDelegate();
|
||||||
public delegate void ScriptMessageDelegate(string message);
|
public delegate void ScriptMessageDelegate(string message);
|
||||||
public delegate void ScriptErrorDelegate(string message, ScriptMessageType messageType);
|
public delegate void ScriptErrorDelegate(string message, ScriptMessageType messageType);
|
||||||
public delegate bool ExecuteDelegate(string batchScript, int num, int lineNumber);
|
public delegate bool ExecuteDelegate(string batchScript, int num, int lineNumber, SqlCmdCommand command);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors / Destructor
|
#region Constructors / Destructor
|
||||||
@@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Take approptiate action on the parsed batches
|
/// Take approptiate action on the parsed batches
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BatchParserAction Go(TextBlock batch, int repeatCount)
|
public BatchParserAction Go(TextBlock batch, int repeatCount, SqlCmdCommand command)
|
||||||
{
|
{
|
||||||
string str;
|
string str;
|
||||||
LineInfo lineInfo;
|
LineInfo lineInfo;
|
||||||
@@ -85,7 +85,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
bool executeResult = false;
|
bool executeResult = false;
|
||||||
if (executeDelegate != null)
|
if (executeDelegate != null)
|
||||||
{
|
{
|
||||||
executeResult = executeDelegate(str, repeatCount, lineInfo.GetStreamPositionForOffset(0).Line + startingLine - 1);
|
executeResult = executeDelegate(str, repeatCount, lineInfo.GetStreamPositionForOffset(0).Line + startingLine - 1, command);
|
||||||
}
|
}
|
||||||
return executeResult ? BatchParserAction.Continue : BatchParserAction.Abort;
|
return executeResult ? BatchParserAction.Continue : BatchParserAction.Abort;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
|
|
||||||
private readonly Batch batch = null;
|
private readonly Batch batch = null;
|
||||||
private readonly ScriptExecutionResult result;
|
private readonly ScriptExecutionResult result;
|
||||||
|
private readonly SqlCmdCommand sqlCmdCommand;
|
||||||
|
|
||||||
private BatchParserExecutionFinishedEventArgs()
|
private BatchParserExecutionFinishedEventArgs()
|
||||||
{
|
{
|
||||||
@@ -23,10 +24,11 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor method for the class
|
/// Constructor method for the class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BatchParserExecutionFinishedEventArgs(ScriptExecutionResult batchResult, Batch batch)
|
public BatchParserExecutionFinishedEventArgs(ScriptExecutionResult batchResult, Batch batch, SqlCmdCommand sqlCmdCommand)
|
||||||
{
|
{
|
||||||
this.batch = batch;
|
this.batch = batch;
|
||||||
result = batchResult;
|
result = batchResult;
|
||||||
|
this.sqlCmdCommand = sqlCmdCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Batch Batch
|
public Batch Batch
|
||||||
@@ -44,5 +46,13 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SqlCmdCommand SqlCmdCommand
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return sqlCmdCommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
private Dictionary<string, string> internalVariables = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
|
private Dictionary<string, string> internalVariables = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
private ConnectionChangedDelegate connectionChangedDelegate;
|
private ConnectionChangedDelegate connectionChangedDelegate;
|
||||||
private ErrorActionChangedDelegate errorActionChangedDelegate;
|
private ErrorActionChangedDelegate errorActionChangedDelegate;
|
||||||
|
|
||||||
public delegate void ConnectionChangedDelegate(SqlConnectionStringBuilder connectionstringBuilder);
|
public delegate void ConnectionChangedDelegate(SqlConnectionStringBuilder connectionstringBuilder);
|
||||||
public delegate void ErrorActionChangedDelegate(OnErrorAction ea);
|
public delegate void ErrorActionChangedDelegate(OnErrorAction ea);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor taking a Parser instance
|
/// Constructor taking a Parser instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -49,7 +49,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
get { return errorActionChangedDelegate; }
|
get { return errorActionChangedDelegate; }
|
||||||
set { errorActionChangedDelegate = value; }
|
set { errorActionChangedDelegate = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks for any environment variable or internal variable.
|
/// Looks for any environment variable or internal variable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -105,17 +105,78 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
get { return internalVariables; }
|
get { return internalVariables; }
|
||||||
set { internalVariables = value; }
|
set { internalVariables = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "ppIBatchSource")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "ppIBatchSource")]
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fileName")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fileName")]
|
||||||
public override BatchParserAction Include(TextBlock filename, out TextReader stream, out string newFilename)
|
public override BatchParserAction Include(TextBlock filename, out TextReader stream, out string newFilename)
|
||||||
{
|
{
|
||||||
stream = null;
|
stream = null;
|
||||||
newFilename = null;
|
newFilename = null;
|
||||||
|
LineInfo lineInfo;
|
||||||
|
|
||||||
RaiseScriptError(string.Format(CultureInfo.CurrentCulture, SR.EE_ExecutionError_CommandNotSupported, "Include"), ScriptMessageType.Error);
|
if (filename == null)
|
||||||
return BatchParserAction.Abort;
|
{
|
||||||
|
stream = null;
|
||||||
|
return BatchParserAction.Abort;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename.GetText(resolveVariables: true, text: out newFilename, lineInfo: out lineInfo);
|
||||||
|
string resolvedFileNameWithFullPath = GetFilePath(newFilename);
|
||||||
|
|
||||||
|
if (!File.Exists(resolvedFileNameWithFullPath))
|
||||||
|
{
|
||||||
|
stream = null;
|
||||||
|
return BatchParserAction.Abort;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream = new StreamReader(resolvedFileNameWithFullPath);
|
||||||
|
}
|
||||||
|
return BatchParserAction.Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFilePath(string fileName)
|
||||||
|
{
|
||||||
|
//try appending the file name with current working directory path
|
||||||
|
string fullFileName = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Environment.CurrentDirectory != null && !File.Exists(fileName))
|
||||||
|
{
|
||||||
|
string currentWorkingDirectory = Environment.CurrentDirectory;
|
||||||
|
if (currentWorkingDirectory != null)
|
||||||
|
{
|
||||||
|
fullFileName = Path.GetFullPath(Path.Combine(currentWorkingDirectory, fileName));
|
||||||
|
if (!File.Exists(fullFileName))
|
||||||
|
{
|
||||||
|
fullFileName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (fullFileName == null)
|
||||||
|
{
|
||||||
|
fullFileName = Path.GetFullPath(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullFileName;
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
//path contains invalid path characters.
|
||||||
|
throw new SqlCmdException(SR.SqlCmd_PathInvalid);
|
||||||
|
}
|
||||||
|
catch (PathTooLongException)
|
||||||
|
{
|
||||||
|
//path is too long
|
||||||
|
throw new SqlCmdException(SR.SqlCmd_PathLong);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
//catch all other exceptions and report generic error
|
||||||
|
throw new SqlCmdException(string.Format(SR.SqlCmd_FailedInclude, fileName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -281,14 +281,14 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="batch"></param>
|
/// <param name="batch"></param>
|
||||||
/// <param name="batchResult"></param>
|
/// <param name="batchResult"></param>
|
||||||
private void RaiseBatchParserExecutionFinished(Batch batch, ScriptExecutionResult batchResult)
|
private void RaiseBatchParserExecutionFinished(Batch batch, ScriptExecutionResult batchResult, SqlCmdCommand sqlCmdCommand)
|
||||||
{
|
{
|
||||||
Debug.Assert(batch != null);
|
Debug.Assert(batch != null);
|
||||||
|
|
||||||
EventHandler<BatchParserExecutionFinishedEventArgs> cache = BatchParserExecutionFinished;
|
EventHandler<BatchParserExecutionFinishedEventArgs> cache = BatchParserExecutionFinished;
|
||||||
if (cache != null)
|
if (cache != null)
|
||||||
{
|
{
|
||||||
BatchParserExecutionFinishedEventArgs args = new BatchParserExecutionFinishedEventArgs(batchResult, batch);
|
BatchParserExecutionFinishedEventArgs args = new BatchParserExecutionFinishedEventArgs(batchResult, batch, sqlCmdCommand);
|
||||||
cache(this, args);
|
cache(this, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
private bool ExecuteBatchInternal(
|
private bool ExecuteBatchInternal(
|
||||||
string batchScript,
|
string batchScript,
|
||||||
int num,
|
int num,
|
||||||
int lineNumber)
|
int lineNumber,
|
||||||
|
SqlCmdCommand sqlCmdCommand)
|
||||||
{
|
{
|
||||||
if (lineNumber == -1)
|
if (lineNumber == -1)
|
||||||
{
|
{
|
||||||
@@ -353,7 +354,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
{
|
{
|
||||||
bool continueProcessing = true;
|
bool continueProcessing = true;
|
||||||
numBatchExecutionTimes = num;
|
numBatchExecutionTimes = num;
|
||||||
ExecuteBatchTextSpanInternal(batchScript, localTextSpan, out continueProcessing);
|
ExecuteBatchTextSpanInternal(batchScript, localTextSpan, out continueProcessing, sqlCmdCommand);
|
||||||
return continueProcessing;
|
return continueProcessing;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -368,7 +369,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
/// <param name="batchScript"></param>
|
/// <param name="batchScript"></param>
|
||||||
/// <param name="textSpan"></param>
|
/// <param name="textSpan"></param>
|
||||||
/// <param name="continueProcessing"></param>
|
/// <param name="continueProcessing"></param>
|
||||||
private void ExecuteBatchTextSpanInternal(string batchScript, TextSpan textSpan, out bool continueProcessing)
|
private void ExecuteBatchTextSpanInternal(string batchScript, TextSpan textSpan, out bool continueProcessing, SqlCmdCommand sqlCmdCommand)
|
||||||
{
|
{
|
||||||
Debug.Assert(!String.IsNullOrEmpty(batchScript));
|
Debug.Assert(!String.IsNullOrEmpty(batchScript));
|
||||||
continueProcessing = true;
|
continueProcessing = true;
|
||||||
@@ -443,7 +444,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
|
|
||||||
if (!isExecutionDiscarded)
|
if (!isExecutionDiscarded)
|
||||||
{
|
{
|
||||||
RaiseBatchParserExecutionFinished(currentBatch, batchResult);
|
RaiseBatchParserExecutionFinished(currentBatch, batchResult, sqlCmdCommand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -501,7 +502,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser.ExecutionEngineCode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ExecuteBatchInternal(script, /* num */ 1, /* lineNumber */ 0);
|
ExecuteBatchInternal(script, num: 1, lineNumber: 0, /* sqlcmdCommand required for parsing only*/ sqlCmdCommand: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
{
|
{
|
||||||
public interface ICommandHandler
|
public interface ICommandHandler
|
||||||
{
|
{
|
||||||
BatchParserAction Go(TextBlock batch, int repeatCount);
|
BatchParserAction Go(TextBlock batch, int repeatCount, SqlCmdCommand tokenType);
|
||||||
BatchParserAction OnError(Token token, OnErrorAction action);
|
BatchParserAction OnError(Token token, OnErrorAction action);
|
||||||
BatchParserAction Include(TextBlock filename, out TextReader stream, out string newFilename);
|
BatchParserAction Include(TextBlock filename, out TextReader stream, out string newFilename);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
public class OnErrorSqlCmdCommand : SqlCmdCommand
|
||||||
|
{
|
||||||
|
internal OnErrorSqlCmdCommand(OnErrorAction action) : base(LexerTokenType.OnError)
|
||||||
|
{
|
||||||
|
Action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnErrorAction Action { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
private Lexer lexer;
|
private Lexer lexer;
|
||||||
private List<Token> tokenBuffer;
|
private List<Token> tokenBuffer;
|
||||||
private readonly IVariableResolver variableResolver;
|
private readonly IVariableResolver variableResolver;
|
||||||
|
private SqlCmdCommand sqlCmdCommand;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor for the Parser class
|
/// Constructor for the Parser class
|
||||||
@@ -126,7 +127,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
if (state == 0 && ch == '$')
|
if (state == 0 && ch == '$')
|
||||||
{
|
{
|
||||||
state = 1;
|
state = 1;
|
||||||
}
|
}
|
||||||
else if (state == 1)
|
else if (state == 1)
|
||||||
{
|
{
|
||||||
if (ch == '(')
|
if (ch == '(')
|
||||||
@@ -200,7 +201,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
private void ExecuteBatch(int repeatCount)
|
private void ExecuteBatch(int repeatCount)
|
||||||
{
|
{
|
||||||
BatchParserAction action;
|
BatchParserAction action;
|
||||||
action = commandHandler.Go(new TextBlock(this, tokenBuffer), repeatCount);
|
action = commandHandler.Go(new TextBlock(this, tokenBuffer), repeatCount, this.sqlCmdCommand);
|
||||||
|
|
||||||
if (action == BatchParserAction.Abort)
|
if (action == BatchParserAction.Abort)
|
||||||
{
|
{
|
||||||
@@ -292,8 +293,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
if (tokenText.IndexOf('"') != -1)
|
if (tokenText.IndexOf('"') != -1)
|
||||||
{
|
{
|
||||||
tokens = SplitQuotedTextToken(token);
|
tokens = SplitQuotedTextToken(token);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tokens = new[] { token };
|
tokens = new[] { token };
|
||||||
}
|
}
|
||||||
@@ -348,7 +349,7 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
tokens.Add(GetSubToken(token, quotePos + 1, closingQuotePos, LexerTokenType.TextVerbatim));
|
tokens.Add(GetSubToken(token, quotePos + 1, closingQuotePos, LexerTokenType.TextVerbatim));
|
||||||
}
|
}
|
||||||
offset = closingQuotePos + 1;
|
offset = closingQuotePos + 1;
|
||||||
|
|
||||||
quotePos = tokenText.IndexOf('"', offset);
|
quotePos = tokenText.IndexOf('"', offset);
|
||||||
}
|
}
|
||||||
if (offset != tokenText.Length)
|
if (offset != tokenText.Length)
|
||||||
@@ -406,6 +407,11 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
ParseSetvar(setvarToken);
|
ParseSetvar(setvarToken);
|
||||||
break;
|
break;
|
||||||
case LexerTokenType.Connect:
|
case LexerTokenType.Connect:
|
||||||
|
Token connectToken = LookaheadToken;
|
||||||
|
RemoveLastWhitespaceToken();
|
||||||
|
Accept();
|
||||||
|
ParseConnect(connectToken);
|
||||||
|
break;
|
||||||
case LexerTokenType.Ed:
|
case LexerTokenType.Ed:
|
||||||
case LexerTokenType.ErrorCommand:
|
case LexerTokenType.ErrorCommand:
|
||||||
case LexerTokenType.Execute:
|
case LexerTokenType.Execute:
|
||||||
@@ -472,6 +478,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
|
|
||||||
parserAction = commandHandler.OnError(onErrorToken, onErrorAction);
|
parserAction = commandHandler.OnError(onErrorToken, onErrorAction);
|
||||||
|
|
||||||
|
this.sqlCmdCommand = new OnErrorSqlCmdCommand(onErrorAction);
|
||||||
|
|
||||||
if (parserAction == BatchParserAction.Abort)
|
if (parserAction == BatchParserAction.Abort)
|
||||||
{
|
{
|
||||||
RaiseError(ErrorCode.Aborted);
|
RaiseError(ErrorCode.Aborted);
|
||||||
@@ -522,6 +530,83 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
variableResolver.SetVariable(setvarToken.Begin, variableName, variableValue);
|
variableResolver.SetVariable(setvarToken.Begin, variableName, variableValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ParseConnect(Token connectToken)
|
||||||
|
{
|
||||||
|
string serverName = null;
|
||||||
|
string userName = null;
|
||||||
|
string password = null;
|
||||||
|
Accept(LexerTokenType.Whitespace);
|
||||||
|
Expect(LexerTokenType.Text);
|
||||||
|
|
||||||
|
serverName = ResolveVariables(LookaheadToken, 0, null);
|
||||||
|
if (serverName == null)
|
||||||
|
{
|
||||||
|
//found some text but couldn't parse for servername
|
||||||
|
RaiseError(ErrorCode.UnrecognizedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
Accept();
|
||||||
|
Accept(LexerTokenType.Whitespace);
|
||||||
|
|
||||||
|
switch (LookaheadTokenType)
|
||||||
|
{
|
||||||
|
case LexerTokenType.Text:
|
||||||
|
userName = ParseUserName();
|
||||||
|
password = ParsePassword();
|
||||||
|
if(userName == null || password == null)
|
||||||
|
{
|
||||||
|
//found some text but couldn't parse for user/password
|
||||||
|
RaiseError(ErrorCode.UnrecognizedToken);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LexerTokenType.NewLine:
|
||||||
|
case LexerTokenType.Eof:
|
||||||
|
Accept();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RaiseError(ErrorCode.UnrecognizedToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sqlCmdCommand = new ConnectSqlCmdCommand(serverName, userName, password);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ParseUserName()
|
||||||
|
{
|
||||||
|
string username = null;
|
||||||
|
if (LookaheadToken.Text == "-U")
|
||||||
|
{
|
||||||
|
Accept();
|
||||||
|
Accept(LexerTokenType.Whitespace);
|
||||||
|
if (LookaheadTokenType == LexerTokenType.Text)
|
||||||
|
{
|
||||||
|
username = ResolveVariables(LookaheadToken, 0, null);
|
||||||
|
Accept();
|
||||||
|
Accept(LexerTokenType.Whitespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ParsePassword()
|
||||||
|
{
|
||||||
|
string password = null;
|
||||||
|
if (LookaheadToken.Text == "-P")
|
||||||
|
{
|
||||||
|
Accept();
|
||||||
|
Accept(LexerTokenType.Whitespace);
|
||||||
|
if (LookaheadTokenType == LexerTokenType.Text)
|
||||||
|
{
|
||||||
|
password = ResolveVariables(LookaheadToken, 0, null);
|
||||||
|
Accept();
|
||||||
|
Accept(LexerTokenType.Whitespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
internal void RaiseError(ErrorCode errorCode, string message = null)
|
internal void RaiseError(ErrorCode errorCode, string message = null)
|
||||||
{
|
{
|
||||||
RaiseError(errorCode, LookaheadToken, message);
|
RaiseError(errorCode, LookaheadToken, message);
|
||||||
@@ -558,8 +643,8 @@ namespace Microsoft.SqlTools.ServiceLayer.BatchParser
|
|||||||
|
|
||||||
if (variablePos != null)
|
if (variablePos != null)
|
||||||
{
|
{
|
||||||
LineInfo.CalculateLineColumnForOffset(inputToken.Text, reference.Start - offset,
|
LineInfo.CalculateLineColumnForOffset(inputToken.Text, reference.Start - offset,
|
||||||
variablePos.Value.Offset, variablePos.Value.Line, variablePos.Value.Column,
|
variablePos.Value.Offset, variablePos.Value.Line, variablePos.Value.Column,
|
||||||
out line, out column);
|
out line, out column);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class to pass back SqlCmd specific properties from Parser to Query Execution
|
||||||
|
/// </summary>
|
||||||
|
public class SqlCmdCommand
|
||||||
|
{
|
||||||
|
internal SqlCmdCommand(LexerTokenType tokenType)
|
||||||
|
{
|
||||||
|
this.LexerTokenType = tokenType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LexerTokenType LexerTokenType { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specific exception type for SQLCMD related issues
|
||||||
|
/// </summary>
|
||||||
|
public class SqlCmdException : Exception
|
||||||
|
{
|
||||||
|
public SqlCmdException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -261,6 +261,30 @@ namespace Microsoft.SqlTools.ManagedBatchParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string SqlCmd_PathInvalid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Keys.GetString(Keys.SqlCmd_PathInvalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SqlCmd_PathLong
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Keys.GetString(Keys.SqlCmd_PathLong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SqlCmd_FailedInclude
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Keys.GetString(Keys.SqlCmd_FailedInclude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
public class Keys
|
public class Keys
|
||||||
{
|
{
|
||||||
@@ -356,6 +380,14 @@ namespace Microsoft.SqlTools.ManagedBatchParser
|
|||||||
public const string BatchParser_VariableNotDefined = "BatchParser_VariableNotDefined";
|
public const string BatchParser_VariableNotDefined = "BatchParser_VariableNotDefined";
|
||||||
|
|
||||||
|
|
||||||
|
public const string SqlCmd_PathInvalid = "SqlCmd_PathInvalid";
|
||||||
|
|
||||||
|
|
||||||
|
public const string SqlCmd_PathLong = "SqlCmd_PathLong";
|
||||||
|
|
||||||
|
|
||||||
|
public const string SqlCmd_FailedInclude = "SqlCmd_FailedInclude";
|
||||||
|
|
||||||
private Keys()
|
private Keys()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|||||||
@@ -233,4 +233,16 @@
|
|||||||
<value>Variable {0} is not defined.</value>
|
<value>Variable {0} is not defined.</value>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SqlCmd_PathInvalid" xml:space="preserve">
|
||||||
|
<value>Path contains invalid characters.</value>
|
||||||
|
<comment></comment>
|
||||||
|
</data>
|
||||||
|
<data name="SqlCmd_PathLong" xml:space="preserve">
|
||||||
|
<value>Path too long.</value>
|
||||||
|
<comment></comment>
|
||||||
|
</data>
|
||||||
|
<data name="SqlCmd_FailedInclude" xml:space="preserve">
|
||||||
|
<value>Could not find included file {0}.</value>
|
||||||
|
<comment></comment>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -82,3 +82,9 @@ BatchParser_IncorrectSyntax = Incorrect syntax was encountered while parsing '{0
|
|||||||
|
|
||||||
BatchParser_VariableNotDefined = Variable {0} is not defined.
|
BatchParser_VariableNotDefined = Variable {0} is not defined.
|
||||||
|
|
||||||
|
SqlCmd_PathInvalid = Path contains invalid characters.
|
||||||
|
|
||||||
|
SqlCmd_PathLong = Path too long.
|
||||||
|
|
||||||
|
SqlCmd_FailedInclude = Could not find included file {0}.
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,21 @@
|
|||||||
<target state="new">Variable {0} is not defined.</target>
|
<target state="new">Variable {0} is not defined.</target>
|
||||||
<note></note>
|
<note></note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="SqlCmd_PathInvalid">
|
||||||
|
<source>Path contains invalid characters.</source>
|
||||||
|
<target state="new">Path contains invalid characters.</target>
|
||||||
|
<note></note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="SqlCmd_PathLong">
|
||||||
|
<source>Path too long.</source>
|
||||||
|
<target state="new">Path too long.</target>
|
||||||
|
<note></note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="SqlCmd_FailedInclude">
|
||||||
|
<source>Could not find included file {0}.</source>
|
||||||
|
<target state="new">Could not find included file {0}.</target>
|
||||||
|
<note></note>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
@@ -355,6 +355,21 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
{
|
{
|
||||||
return Keys.GetString(Keys.QueryServiceExecutionPlanNotFound);
|
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
|
public static string PeekDefinitionNoResultsError
|
||||||
@@ -3277,7 +3292,13 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
public const string QueryServiceResultSetNoColumnSchema = "QueryServiceResultSetNoColumnSchema";
|
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";
|
public const string SerializationServiceUnsupportedFormat = "SerializationServiceUnsupportedFormat";
|
||||||
|
|||||||
@@ -1,63 +1,63 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype=">text/microsoft-resx</resheader>
|
<resheader name="resmimetype=">text/microsoft-resx</resheader>
|
||||||
<resheader name="version=">2.0</resheader>
|
<resheader name="version=">2.0</resheader>
|
||||||
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
<resheader name="writer=">System.Resources.ResXResourceWriter, 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="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="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
|
||||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
|
<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>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: 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: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: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>
|
<value>Could not retrieve an execution plan from the result set </value>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
</data>
|
</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">
|
<data name="SerializationServiceUnsupportedFormat" xml:space="preserve">
|
||||||
<value>Unsupported Save Format: {0}</value>
|
<value>Unsupported Save Format: {0}</value>
|
||||||
<comment>.
|
<comment>.
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ QueryServiceResultSetNoColumnSchema = Could not retrieve column schema for resul
|
|||||||
|
|
||||||
QueryServiceExecutionPlanNotFound = Could not retrieve an execution plan from the result set
|
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
|
# Serialization Service
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ using Microsoft.SqlTools.Utility;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.BatchParser;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||||
{
|
{
|
||||||
@@ -71,7 +72,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
internal Batch(string batchText, SelectionData selection, int ordinalId,
|
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
|
// Sanity check for input
|
||||||
Validate.IsNotNullOrEmptyString(nameof(batchText), batchText);
|
Validate.IsNotNullOrEmptyString(nameof(batchText), batchText);
|
||||||
@@ -138,6 +146,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// called from the Batch but from the ResultSet instance.
|
/// called from the Batch but from the ResultSet instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event ResultSet.ResultSetAsyncEventHandler ResultSetUpdated;
|
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
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
@@ -147,6 +161,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string BatchText { get; set; }
|
public string BatchText { get; set; }
|
||||||
|
|
||||||
|
public SqlCmdCommand SqlCmdCommand { get; set; }
|
||||||
|
|
||||||
public int BatchExecutionCount { get; private set; }
|
public int BatchExecutionCount { get; private set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Localized timestamp for when the execution completed.
|
/// Localized timestamp for when the execution completed.
|
||||||
@@ -243,13 +259,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Methods
|
#region Public Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes this batch and captures any server messages that are returned.
|
/// Executes this batch and captures any server messages that are returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="conn">The connection to use to execute the batch</param>
|
/// <param name="conn">The connection to use to execute the batch</param>
|
||||||
/// <param name="cancellationToken">Token for cancelling the execution</param>
|
/// <param name="cancellationToken">Token for cancelling the execution</param>
|
||||||
public async Task Execute(DbConnection conn, CancellationToken cancellationToken)
|
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
|
// Sanity check to make sure we haven't already run this batch
|
||||||
if (HasExecuted)
|
if (HasExecuted)
|
||||||
@@ -270,10 +297,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
{
|
{
|
||||||
sqlConn.GetUnderlyingConnection().InfoMessage += ServerMessageHandler;
|
sqlConn.GetUnderlyingConnection().InfoMessage += ServerMessageHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await DoExecute(conn, cancellationToken);
|
await DoExecute(conn, cancellationToken, onErrorAction);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
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;
|
bool canContinue = true;
|
||||||
int timesLoop = this.BatchExecutionCount;
|
int timesLoop = this.BatchExecutionCount;
|
||||||
@@ -326,6 +353,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
catch (DbException dbe)
|
catch (DbException dbe)
|
||||||
{
|
{
|
||||||
HasError = true;
|
HasError = true;
|
||||||
|
if (onErrorAction == OnErrorAction.Exit)
|
||||||
|
{
|
||||||
|
throw new SqlCmdException(dbe.Message);
|
||||||
|
}
|
||||||
canContinue = await UnwrapDbException(dbe);
|
canContinue = await UnwrapDbException(dbe);
|
||||||
if (canContinue)
|
if (canContinue)
|
||||||
{
|
{
|
||||||
@@ -445,7 +476,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
private void ExtendResultMetadata(List<DbColumn[]> columnSchemas, List<ResultSet> results)
|
private void ExtendResultMetadata(List<DbColumn[]> columnSchemas, List<ResultSet> results)
|
||||||
{
|
{
|
||||||
if (columnSchemas.Count != results.Count)
|
if (columnSchemas.Count != results.Count)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,6 +710,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
{
|
{
|
||||||
this.HasError = true;
|
this.HasError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.HandleOnErrorAction != null)
|
||||||
|
{
|
||||||
|
HandleOnErrorAction(this, isError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -693,6 +729,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
{
|
{
|
||||||
bool canIgnore = true;
|
bool canIgnore = true;
|
||||||
SqlException se = dbe as SqlException;
|
SqlException se = dbe as SqlException;
|
||||||
|
|
||||||
if (se != null)
|
if (se != null)
|
||||||
{
|
{
|
||||||
var errors = se.Errors.Cast<SqlError>().ToList();
|
var errors = se.Errors.Cast<SqlError>().ToList();
|
||||||
@@ -729,7 +766,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private SpecialAction ProcessResultSetSpecialActions()
|
private SpecialAction ProcessResultSetSpecialActions()
|
||||||
{
|
{
|
||||||
foreach (ResultSet resultSet in resultSets)
|
foreach (ResultSet resultSet in resultSets)
|
||||||
{
|
{
|
||||||
specialAction.CombineSpecialAction(resultSet.Summary.SpecialAction);
|
specialAction.CombineSpecialAction(resultSet.Summary.SpecialAction);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
public class Query : IDisposable
|
public class Query : IDisposable
|
||||||
{
|
{
|
||||||
#region Constants
|
#region Constants
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Error" code produced by SQL Server when the database context (name) for a connection changes.
|
/// "Error" code produced by SQL Server when the database context (name) for a connection changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int DatabaseContextChangeErrorNumber = 5701;
|
private const int DatabaseContextChangeErrorNumber = 5701;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ON keyword
|
/// ON keyword
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -45,7 +45,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// OFF keyword
|
/// OFF keyword
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string Off = "OFF";
|
private const string Off = "OFF";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// showplan_xml statement
|
/// showplan_xml statement
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// statistics xml statement
|
/// statistics xml statement
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const string SetStatisticsXml = "SET STATISTICS XML {0}";
|
private const string SetStatisticsXml = "SET STATISTICS XML {0}";
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Member Variables
|
#region Member Variables
|
||||||
@@ -84,7 +84,23 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the new database if the database name was changed in the query
|
/// Name of the new database if the database name was changed in the query
|
||||||
/// </summary>
|
/// </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
|
#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="settings">Settings for how to execute the query, from the user</param>
|
||||||
/// <param name="outputFactory">Factory for creating output files</param>
|
/// <param name="outputFactory">Factory for creating output files</param>
|
||||||
public Query(
|
public Query(
|
||||||
string queryText,
|
string queryText,
|
||||||
ConnectionInfo connection,
|
ConnectionInfo connection,
|
||||||
QueryExecutionSettings settings,
|
QueryExecutionSettings settings,
|
||||||
IFileStreamFactory outputFactory,
|
IFileStreamFactory outputFactory,
|
||||||
bool getFullColumnSchema = false,
|
bool getFullColumnSchema = false,
|
||||||
bool applyExecutionSettings = false)
|
bool applyExecutionSettings = false)
|
||||||
@@ -125,13 +141,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
|
|
||||||
var batchSelection = parserResult
|
var batchSelection = parserResult
|
||||||
.Select((batchDefinition, index) =>
|
.Select((batchDefinition, index) =>
|
||||||
new Batch(batchDefinition.BatchText,
|
new Batch(batchDefinition.BatchText,
|
||||||
new SelectionData(
|
new SelectionData(
|
||||||
batchDefinition.StartLine-1,
|
batchDefinition.StartLine-1,
|
||||||
batchDefinition.StartColumn-1,
|
batchDefinition.StartColumn-1,
|
||||||
batchDefinition.EndLine-1,
|
batchDefinition.EndLine-1,
|
||||||
batchDefinition.EndColumn-1),
|
batchDefinition.EndColumn-1),
|
||||||
index, outputFactory,
|
index, outputFactory,
|
||||||
|
batchDefinition.SqlCmdCommand,
|
||||||
batchDefinition.BatchExecutionCount,
|
batchDefinition.BatchExecutionCount,
|
||||||
getFullColumnSchema));
|
getFullColumnSchema));
|
||||||
|
|
||||||
@@ -154,14 +171,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query that completed</param>
|
/// <param name="query">The query that completed</param>
|
||||||
public delegate Task QueryAsyncEventHandler(Query query);
|
public delegate Task QueryAsyncEventHandler(Query query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate type for callback when a query fails
|
/// Delegate type for callback when a query fails
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">Query that raised the event</param>
|
/// <param name="query">Query that raised the event</param>
|
||||||
/// <param name="exception">Exception that caused the query to fail</param>
|
/// <param name="exception">Exception that caused the query to fail</param>
|
||||||
public delegate Task QueryAsyncErrorEventHandler(Query query, Exception exception);
|
public delegate Task QueryAsyncErrorEventHandler(Query query, Exception exception);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event to be called when a batch is completed.
|
/// Event to be called when a batch is completed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -356,7 +373,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="successHandler">Delegate to call when the request completes successfully</param>
|
/// <param name="successHandler">Delegate to call when the request completes successfully</param>
|
||||||
/// <param name="failureHandler">Delegate to call if the request fails</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)
|
ResultSet.SaveAsAsyncEventHandler successHandler, ResultSet.SaveAsFailureAsyncEventHandler failureHandler)
|
||||||
{
|
{
|
||||||
// Sanity check to make sure that the batch is within bounds
|
// 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
|
// Mark that we've internally executed
|
||||||
hasExecuteBeenCalled = true;
|
hasExecuteBeenCalled = true;
|
||||||
|
|
||||||
// Don't actually execute if there aren't any batches to execute
|
// Don't actually execute if there aren't any batches to execute
|
||||||
if (Batches.Length == 0)
|
if (Batches.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -399,9 +416,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locate and setup the connection
|
// 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;
|
sqlConn = queryConnection as ReliableSqlConnection;
|
||||||
if (sqlConn != null)
|
if (sqlConn != null)
|
||||||
{
|
{
|
||||||
@@ -410,7 +428,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
sqlConn.GetUnderlyingConnection().InfoMessage += OnInfoMessage;
|
sqlConn.GetUnderlyingConnection().InfoMessage += OnInfoMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Execute beforeBatches synchronously, before the user defined batches
|
// Execute beforeBatches synchronously, before the user defined batches
|
||||||
foreach (Batch b in BeforeBatches)
|
foreach (Batch b in BeforeBatches)
|
||||||
{
|
{
|
||||||
@@ -427,7 +445,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
b.ResultSetCompletion += ResultSetCompleted;
|
b.ResultSetCompletion += ResultSetCompleted;
|
||||||
b.ResultSetAvailable += ResultSetAvailable;
|
b.ResultSetAvailable += ResultSetAvailable;
|
||||||
b.ResultSetUpdated += ResultSetUpdated;
|
b.ResultSetUpdated += ResultSetUpdated;
|
||||||
await b.Execute(queryConnection, cancellationSource.Token);
|
|
||||||
|
await ExecuteBatch(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute afterBatches synchronously, after the user defined batches
|
// Execute afterBatches synchronously, after the user defined batches
|
||||||
@@ -445,7 +464,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
HasErrored = true;
|
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));
|
await BatchMessageSent(new ResultMessage(SR.QueryServiceQueryCancelled, false, null));
|
||||||
}
|
}
|
||||||
@@ -463,7 +486,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
// Subscribe to database informational messages
|
// Subscribe to database informational messages
|
||||||
sqlConn.GetUnderlyingConnection().InfoMessage -= OnInfoMessage;
|
sqlConn.GetUnderlyingConnection().InfoMessage -= OnInfoMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any message notified us we had changed databases, then we must let the connection service know
|
// If any message notified us we had changed databases, then we must let the connection service know
|
||||||
if (newDatabaseName != null)
|
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>
|
/// <summary>
|
||||||
/// Handler for database messages during query execution
|
/// Handler for database messages during query execution
|
||||||
@@ -511,8 +591,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyExecutionSettings(
|
private void ApplyExecutionSettings(
|
||||||
ConnectionInfo connection,
|
ConnectionInfo connection,
|
||||||
QueryExecutionSettings settings,
|
QueryExecutionSettings settings,
|
||||||
IFileStreamFactory outputFactory)
|
IFileStreamFactory outputFactory)
|
||||||
{
|
{
|
||||||
outputFactory.QueryExecutionSettings = settings;
|
outputFactory.QueryExecutionSettings = settings;
|
||||||
@@ -549,7 +629,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (settings.StatisticsIO)
|
if (settings.StatisticsIO)
|
||||||
{
|
{
|
||||||
builderBefore.AppendFormat("{0} ", helper.GetSetStatisticsIOString(true));
|
builderBefore.AppendFormat("{0} ", helper.GetSetStatisticsIOString(true));
|
||||||
builderAfter.AppendFormat("{0} ", helper.GetSetStatisticsIOString (false));
|
builderAfter.AppendFormat("{0} ", helper.GetSetStatisticsIOString (false));
|
||||||
}
|
}
|
||||||
@@ -562,35 +642,35 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (settings.ParseOnly)
|
if (settings.ParseOnly)
|
||||||
{
|
{
|
||||||
builderBefore.AppendFormat("{0} ", helper.GetSetParseOnlyString(true));
|
builderBefore.AppendFormat("{0} ", helper.GetSetParseOnlyString(true));
|
||||||
builderAfter.AppendFormat("{0} ", helper.GetSetParseOnlyString(false));
|
builderAfter.AppendFormat("{0} ", helper.GetSetParseOnlyString(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// append first part of exec options
|
// append first part of exec options
|
||||||
builderBefore.AppendFormat("{0} {1} {2}",
|
builderBefore.AppendFormat("{0} {1} {2}",
|
||||||
helper.SetRowCountString, helper.SetTextSizeString, helper.SetNoCountString);
|
helper.SetRowCountString, helper.SetTextSizeString, helper.SetNoCountString);
|
||||||
|
|
||||||
if (!connection.IsSqlDW)
|
if (!connection.IsSqlDW)
|
||||||
{
|
{
|
||||||
// append second part of exec options
|
// append second part of exec options
|
||||||
builderBefore.AppendFormat(" {0} {1} {2} {3} {4} {5} {6}",
|
builderBefore.AppendFormat(" {0} {1} {2} {3} {4} {5} {6}",
|
||||||
helper.SetConcatenationNullString,
|
helper.SetConcatenationNullString,
|
||||||
helper.SetArithAbortString,
|
helper.SetArithAbortString,
|
||||||
helper.SetLockTimeoutString,
|
helper.SetLockTimeoutString,
|
||||||
helper.SetQueryGovernorCostString,
|
helper.SetQueryGovernorCostString,
|
||||||
helper.SetDeadlockPriorityString,
|
helper.SetDeadlockPriorityString,
|
||||||
helper.SetTransactionIsolationLevelString,
|
helper.SetTransactionIsolationLevelString,
|
||||||
// We treat XACT_ABORT special in that we don't add anything if the option
|
// 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
|
// 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
|
// 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
|
// set and having a client change it could be dangerous (the reverse is much
|
||||||
// less risky)
|
// less risky)
|
||||||
|
|
||||||
// The full fix would probably be to make the options tri-state instead of
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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)
|
// do it silently (since the default is going to be off)
|
||||||
@@ -655,7 +735,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Does this connection support XML Execution plans
|
/// Does this connection support XML Execution plans
|
||||||
/// </summary>
|
/// </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)
|
// Determining which execution plan options may be applied (may be added to for pre-yukon support)
|
||||||
return (!connectionInfo.IsSqlDW && connectionInfo.MajorVersion >= 9);
|
return (!connectionInfo.IsSqlDW && connectionInfo.MajorVersion >= 9);
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ namespace Microsoft.SqlTools.ManagedBatchParser.UnitTests.BatchParser
|
|||||||
var executionResult = batch.Execute(sqlConn, ShowPlanType.AllShowPlan);
|
var executionResult = batch.Execute(sqlConn, ShowPlanType.AllShowPlan);
|
||||||
Assert.Equal<ScriptExecutionResult>(ScriptExecutionResult.Success, executionResult);
|
Assert.Equal<ScriptExecutionResult>(ScriptExecutionResult.Success, executionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the exeception is handled by passing invalid keyword.
|
// Verify the exeception is handled by passing invalid keyword.
|
||||||
@@ -224,7 +224,7 @@ namespace Microsoft.SqlTools.ManagedBatchParser.UnitTests.BatchParser
|
|||||||
Assert.True(false, $"Unexpected error consuming token : {e.Message}");
|
Assert.True(false, $"Unexpected error consuming token : {e.Message}");
|
||||||
}
|
}
|
||||||
// we doesn't expect any exception or testCase failures
|
// we doesn't expect any exception or testCase failures
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test case is to verify that, Powershell's Invoke-SqlCmd handles ":!!if" in an inconsistent way
|
// This test case is to verify that, Powershell's Invoke-SqlCmd handles ":!!if" in an inconsistent way
|
||||||
@@ -362,6 +362,129 @@ GO";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify whether the batchParser parsed :connect command successfully
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void VerifyConnectSqlCmd()
|
||||||
|
{
|
||||||
|
using (ExecutionEngine executionEngine = new ExecutionEngine())
|
||||||
|
{
|
||||||
|
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||||
|
string serverName = liveConnection.ConnectionInfo.ConnectionDetails.ServerName;
|
||||||
|
string userName = liveConnection.ConnectionInfo.ConnectionDetails.UserName;
|
||||||
|
string password = liveConnection.ConnectionInfo.ConnectionDetails.Password;
|
||||||
|
string sqlCmdQuery = $@"
|
||||||
|
:Connect {serverName} -U {userName} -P {password}
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
string sqlCmdQueryIncorrect = $@"
|
||||||
|
:Connect {serverName} -u {userName} -p {password}
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
var condition = new ExecutionEngineConditions() { IsSqlCmd = true };
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(liveConnection.ConnectionInfo))
|
||||||
|
using (TestExecutor testExecutor = new TestExecutor(sqlCmdQuery, sqlConn, condition))
|
||||||
|
{
|
||||||
|
testExecutor.Run();
|
||||||
|
Assert.True(testExecutor.ParserExecutionError == false, "Parse Execution error should be false");
|
||||||
|
Assert.True(testExecutor.ResultCountQueue.Count == 1, $"Unexpected number of ResultCount items - expected 1 but got {testExecutor.ResultCountQueue.Count}");
|
||||||
|
Assert.True(testExecutor.ErrorMessageQueue.Count == 0, $"Unexpected error messages from test executor : {string.Join(Environment.NewLine, testExecutor.ErrorMessageQueue)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(liveConnection.ConnectionInfo))
|
||||||
|
using (TestExecutor testExecutor = new TestExecutor(sqlCmdQueryIncorrect, sqlConn, condition))
|
||||||
|
{
|
||||||
|
testExecutor.Run();
|
||||||
|
|
||||||
|
Assert.True(testExecutor.ParserExecutionError == true, "Parse Execution error should be true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify whether the batchParser parsed :on error successfully
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void VerifyOnErrorSqlCmd()
|
||||||
|
{
|
||||||
|
using (ExecutionEngine executionEngine = new ExecutionEngine())
|
||||||
|
{
|
||||||
|
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||||
|
string serverName = liveConnection.ConnectionInfo.ConnectionDetails.ServerName;
|
||||||
|
string sqlCmdQuery = $@"
|
||||||
|
:on error ignore
|
||||||
|
GO
|
||||||
|
select * from sys.databases_wrong where name = 'master'
|
||||||
|
GO
|
||||||
|
select* from sys.databases where name = 'master'
|
||||||
|
GO
|
||||||
|
:on error exit
|
||||||
|
GO
|
||||||
|
select* from sys.databases_wrong where name = 'master'
|
||||||
|
GO
|
||||||
|
select* from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
var condition = new ExecutionEngineConditions() { IsSqlCmd = true };
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(liveConnection.ConnectionInfo))
|
||||||
|
using (TestExecutor testExecutor = new TestExecutor(sqlCmdQuery, sqlConn, condition))
|
||||||
|
{
|
||||||
|
testExecutor.Run();
|
||||||
|
|
||||||
|
Assert.True(testExecutor.ResultCountQueue.Count == 1, $"Unexpected number of ResultCount items - expected only 1 since the later should not be executed but got {testExecutor.ResultCountQueue.Count}");
|
||||||
|
Assert.True(testExecutor.ErrorMessageQueue.Count == 2, $"Unexpected number error messages from test executor expected 2, actual : {string.Join(Environment.NewLine, testExecutor.ErrorMessageQueue)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify whether the batchParser parses Include command i.e. :r successfully
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void VerifyIncludeSqlCmd()
|
||||||
|
{
|
||||||
|
string file = "VerifyIncludeSqlCmd_test.sql";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ExecutionEngine executionEngine = new ExecutionEngine())
|
||||||
|
{
|
||||||
|
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||||
|
string serverName = liveConnection.ConnectionInfo.ConnectionDetails.ServerName;
|
||||||
|
string sqlCmdFile = $@"
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
File.WriteAllText("VerifyIncludeSqlCmd_test.sql", sqlCmdFile);
|
||||||
|
|
||||||
|
string sqlCmdQuery = $@"
|
||||||
|
:r {file}
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
var condition = new ExecutionEngineConditions() { IsSqlCmd = true };
|
||||||
|
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(liveConnection.ConnectionInfo))
|
||||||
|
using (TestExecutor testExecutor = new TestExecutor(sqlCmdQuery, sqlConn, condition))
|
||||||
|
{
|
||||||
|
testExecutor.Run();
|
||||||
|
|
||||||
|
Assert.True(testExecutor.ResultCountQueue.Count == 2, $"Unexpected number of ResultCount items - expected 1 but got {testExecutor.ResultCountQueue.Count}");
|
||||||
|
Assert.True(testExecutor.ErrorMessageQueue.Count == 0, $"Unexpected error messages from test executor : {string.Join(Environment.NewLine, testExecutor.ErrorMessageQueue)}");
|
||||||
|
}
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (File.Exists(file))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify whether the executionEngine execute Batch
|
// Verify whether the executionEngine execute Batch
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyExecuteBatch()
|
public void VerifyExecuteBatch()
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Microsoft.SqlTools.ManagedBatchParser.UnitTests.BatchParser
|
|||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BatchParserAction Go(TextBlock batch, int repeatCount)
|
public BatchParserAction Go(TextBlock batch, int repeatCount, SqlCmdCommand command)
|
||||||
{
|
{
|
||||||
string textWithVariablesResolved;
|
string textWithVariablesResolved;
|
||||||
string textWithVariablesUnresolved;
|
string textWithVariablesUnresolved;
|
||||||
|
|||||||
@@ -115,9 +115,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.QueryExecution
|
|||||||
Assert.True(multipleElapsedTime > elapsedTime);
|
Assert.True(multipleElapsedTime > elapsedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Query CreateAndExecuteQuery(string queryText, ConnectionInfo connectionInfo, IFileStreamFactory fileStreamFactory)
|
public static Query CreateAndExecuteQuery(string queryText, ConnectionInfo connectionInfo, IFileStreamFactory fileStreamFactory, bool IsSqlCmd = false)
|
||||||
{
|
{
|
||||||
Query query = new Query(queryText, connectionInfo, new QueryExecutionSettings(), fileStreamFactory);
|
var settings = new QueryExecutionSettings() { IsSqlCmdMode = IsSqlCmd };
|
||||||
|
Query query = new Query(queryText, connectionInfo, settings, fileStreamFactory);
|
||||||
query.Execute();
|
query.Execute();
|
||||||
query.ExecutionTask.Wait();
|
query.ExecutionTask.Wait();
|
||||||
return query;
|
return query;
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.QueryExecution
|
||||||
|
{
|
||||||
|
public class SqlCmdExecutionTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestConnectSqlCmdCommand()
|
||||||
|
{
|
||||||
|
var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory();
|
||||||
|
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||||
|
ConnectionInfo connInfo = liveConnection.ConnectionInfo;
|
||||||
|
string serverName = liveConnection.ConnectionInfo.ConnectionDetails.ServerName;
|
||||||
|
string sqlCmdQuerySuccess = $@"
|
||||||
|
:Connect {serverName}
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
Query query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQuerySuccess, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches.Length == 1, $"Expected: 1 parsed batch, actual : {query.Batches.Length}");
|
||||||
|
Assert.True(query.Batches[0].HasExecuted && !query.Batches[0].HasError && query.Batches[0].ResultSets.Count == 1, "Query should be executed and have one result set");
|
||||||
|
|
||||||
|
string sqlCmdQueryFilaure = $@"
|
||||||
|
:Connect SomeWrongName
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQueryFilaure, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches.Length == 1, $"Expected: 1 parsed batch, actual : {query.Batches.Length}");
|
||||||
|
Assert.True(query.Batches[0].HasError, "Query should have error");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestOnErrorSqlCmdCommand()
|
||||||
|
{
|
||||||
|
var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory();
|
||||||
|
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||||
|
ConnectionInfo connInfo = liveConnection.ConnectionInfo;
|
||||||
|
string sqlCmdQuerySuccess = $@"
|
||||||
|
:on error ignore
|
||||||
|
GO
|
||||||
|
select * from sys.databases_wrong where name = 'master'
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
Query query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQuerySuccess, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches[0].HasExecuted && query.Batches[0].HasError, "first batch should be executed and have error");
|
||||||
|
Assert.True(query.Batches[1].HasExecuted, "last batch should be executed");
|
||||||
|
|
||||||
|
|
||||||
|
string sqlCmdQueryFilaure = $@"
|
||||||
|
:on error exit
|
||||||
|
GO
|
||||||
|
select * from sys.databases_wrong where name = 'master'
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQueryFilaure, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches[0].HasExecuted && query.Batches[0].HasError, "first batch should be executed and have error");
|
||||||
|
Assert.False(query.Batches[1].HasExecuted, "last batch should NOT be executed");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestIncludeSqlCmdCommand()
|
||||||
|
{
|
||||||
|
var fileStreamFactory = MemoryFileSystem.GetFileStreamFactory();
|
||||||
|
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||||
|
ConnectionInfo connInfo = liveConnection.ConnectionInfo;
|
||||||
|
string path = Path.Combine(Environment.CurrentDirectory, "mysqlfile.sql");
|
||||||
|
string sqlPath = "\"" + path + "\"";
|
||||||
|
|
||||||
|
// correct sql file text
|
||||||
|
string correctfileText = $@"
|
||||||
|
select * from sys.databases where name = 'msdb' or name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
// incorrect sql file text
|
||||||
|
string incorrectfileText = $@"
|
||||||
|
select * from sys.databases_wrong where name = 'msdb' or name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
File.WriteAllText(path, correctfileText);
|
||||||
|
|
||||||
|
string sqlCmdQuerySuccess = $@"
|
||||||
|
:on error exit
|
||||||
|
:setvar mypath {sqlPath}
|
||||||
|
GO
|
||||||
|
:r $(mypath)
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
Query query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQuerySuccess, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches.Length == 2, $"Batches should be parsed and should be 2, actual number {query.Batches.Length}");
|
||||||
|
Assert.True(query.Batches[0].HasExecuted && !query.Batches[0].HasError && query.Batches[0].ResultSets.Count == 1 && query.Batches[0].ResultSets[0].RowCount == 2, "first batch should be executed and have 2 results");
|
||||||
|
Assert.True(query.Batches[1].HasExecuted && !query.Batches[1].HasError && query.Batches[1].ResultSets.Count == 1 && query.Batches[1].ResultSets[0].RowCount == 1, "second batch should be executed and have 1 result");
|
||||||
|
|
||||||
|
|
||||||
|
string sqlCmdQueryFilaure1 = $@"
|
||||||
|
:on error exit
|
||||||
|
:setvar mypath somewrongpath
|
||||||
|
GO
|
||||||
|
:r $(mypath)
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQueryFilaure1, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches.Length == 0, $"Batches should be 0 since parsing was aborted, actual number {query.Batches.Length}");
|
||||||
|
|
||||||
|
File.WriteAllText(path, incorrectfileText);
|
||||||
|
|
||||||
|
string sqlCmdQueryFilaure2 = $@"
|
||||||
|
:on error exit
|
||||||
|
:setvar mypath {sqlPath}
|
||||||
|
GO
|
||||||
|
:r $(mypath)
|
||||||
|
GO
|
||||||
|
select * from sys.databases where name = 'master'
|
||||||
|
GO";
|
||||||
|
|
||||||
|
query = ExecuteTests.CreateAndExecuteQuery(sqlCmdQueryFilaure2, connInfo, fileStreamFactory, IsSqlCmd: true);
|
||||||
|
Assert.True(query.Batches.Length == 2, $"Batches should be parsed and should be 2, actual number {query.Batches.Length}");
|
||||||
|
Assert.True(query.Batches[0].HasExecuted && query.Batches[0].HasError, "first batch should be executed and have error");
|
||||||
|
Assert.True(!query.Batches[1].HasExecuted, "second batch should not get to be executed because of the first error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user