Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/sqlcmd : Enable running scripts with SQLCMD variables - Part 1 #839

Merged
merged 7 commits into from
Aug 9, 2019
19 changes: 18 additions & 1 deletion src/Microsoft.SqlTools.Hosting/Utility/GeneralRequestDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class GeneralRequestDetails
{
public GeneralRequestDetails()
{
Options = new Dictionary<string, object>();
Options = new Dictionary<string, object>(new OptionsKeyComparer());
}
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved

public T GetOptionValue<T>(string name, T defaultValue = default(T))
Expand Down Expand Up @@ -111,4 +111,21 @@ protected void SetOptionValue<T>(string name, T value)
/// </summary>
public Dictionary<string, object> Options { get; set; }
}

/// <summary>
/// Compare class for Execution Setting options to ensure ignoring case in dictionary keys
/// </summary>
public class OptionsKeyComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return string.Equals(x, y, StringComparison.InvariantCultureIgnoreCase);
}

public int GetHashCode(string obj)
{
return obj.ToLowerInvariant().GetHashCode();
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private static List<BatchDefinition> ConvertToBatchDefinitionList(string content
int offset = offsets[0];
int startColumn = batchInfos[0].startColumn;
int count = batchInfos.Count;
string batchText = content.Substring(offset, batchInfos[0].length);
string batchText = batchInfos[0].batchText; // content.Substring(offset, batchInfos[0].length);
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved

// if there's only one batch then the line difference is just startLine
if (count > 1)
Expand Down Expand Up @@ -82,15 +82,15 @@ private static List<BatchDefinition> ConvertToBatchDefinitionList(string content
}

// Generate the rest batch definitions
for (int index = 1; index < count - 1; index++)
for (int index = 1; index < count - 1 ; index++)
{
lineDifference = batchInfos[index + 1].startLine - batchInfos[index].startLine;
position = ReadLines(reader, lineDifference, endLine);
endLine = position.Item1;
endColumn = position.Item2;
offset = offsets[index];
batchText = content.Substring(offset, batchInfos[index].length);
startLine = batchInfos[index].startLine;
batchText = batchInfos[index].batchText;
startLine = batchInfos[index].startLine + 1; // this is done for first batch but was missing for following batches
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
startColumn = batchInfos[index].startColumn;

// make a new batch definition for each batch
Expand All @@ -109,7 +109,7 @@ private static List<BatchDefinition> ConvertToBatchDefinitionList(string content
if (count > 1)
{

batchText = content.Substring(offsets[count-1], batchInfos[count - 1].length);
batchText = batchInfos[count - 1].batchText;
BatchDefinition lastBatchDef = GetLastBatchDefinition(reader, batchInfos[count - 1], batchText);
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
batchDefinitionList.Add(lastBatchDef);
}
Expand Down Expand Up @@ -209,7 +209,7 @@ private static List<int> GetOffsets(string content, IList<BatchInfo> batchInfos)
private static BatchDefinition GetLastBatchDefinition(StringReader reader,
BatchInfo batchInfo, string batchText)
{
int startLine = batchInfo.startLine;
int startLine = batchInfo.startLine + 1;
int startColumn = batchInfo.startColumn;
string prevLine = null;
string line = reader.ReadLine();
Expand Down Expand Up @@ -328,12 +328,12 @@ public BatchParserWrapper()
/// <summary>
/// Takes in a query string and returns a list of BatchDefinitions
/// </summary>
public List<BatchDefinition> GetBatches(string sqlScript)
public List<BatchDefinition> GetBatches(string sqlScript, ExecutionEngineConditions conditions = null)
{
batchInfos = new List<BatchInfo>();

// execute the script - all communication / integration after here happen via event handlers
executionEngine.ParseScript(sqlScript, notificationHandler);
executionEngine.ParseScript(sqlScript, notificationHandler, conditions);

// retrieve a list of BatchDefinitions
List<BatchDefinition> batchDefinitionList = ConvertToBatchDefinitionList(sqlScript, batchInfos);
Expand Down Expand Up @@ -381,7 +381,7 @@ private void OnBatchParserExecutionFinished(object sender, BatchParserExecutionF
}

// Add the script info
batchInfos.Add(new BatchInfo(args.Batch.TextSpan.iStartLine, args.Batch.TextSpan.iStartIndex, batchTextLength, args.Batch.ExpectedExecutionCount));
batchInfos.Add(new BatchInfo(args.Batch.TextSpan.iStartLine, args.Batch.TextSpan.iStartIndex, batchTextLength, batchText, args.Batch.ExpectedExecutionCount));
}
}
catch (NotImplementedException)
Expand Down Expand Up @@ -474,17 +474,19 @@ private void Dispose(bool disposing)

private class BatchInfo
{
public BatchInfo(int startLine, int startColumn, int length, int repeatCount = 1)
public BatchInfo(int startLine, int startColumn, int length, string batchText, int repeatCount = 1)
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
{
this.startLine = startLine;
this.startColumn = startColumn;
this.length = length;
this.executionCount = repeatCount;
this.batchText = batchText;
}
public int startLine;
public int startColumn;
public int length;
public int executionCount;
public string batchText;
}
pensivebrian marked this conversation as resolved.
Show resolved Hide resolved

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private void ConfigureBatchParser()
batchParser.HaltParser = new BatchParser.HaltParserDelegate(OnHaltParser);
batchParser.StartingLine = startingLine;

if (isLocalParse)
if (isLocalParse && !sqlCmdMode)
{
batchParser.DisableVariableSubstitution();
}
Expand Down Expand Up @@ -1045,23 +1045,27 @@ public void ExecuteBatch(ScriptExecutionArgs scriptExecutionArgs)
/// Parses the script locally
/// </summary>
/// <param name="script">script to parse</param>
/// <param name="batchEventsHandler">batch handler</param>
/// <param name="batchEventsHandler">batch handler</param>
/// <param name="conditions">execution engine conditions if specified</param>
/// <remarks>
/// The batch parser functionality is used in this case
/// </remarks>
public void ParseScript(string script, IBatchEventsHandler batchEventsHandler)
public void ParseScript(string script, IBatchEventsHandler batchEventsHandler, ExecutionEngineConditions conditions = null)
{
Validate.IsNotNull(nameof(script), script);
Validate.IsNotNull(nameof(batchEventsHandler), batchEventsHandler);


if (conditions != null)
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
{
this.conditions = conditions;
}
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
this.script = script;
batchEventHandlers = batchEventsHandler;
isLocalParse = true;

DoExecute(/* isBatchParser */ true);
}

/// <summary>
/// Close the current connection
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,16 @@ public static LanguageService Instance

#endregion

#region Private / internal instance fields and constructor
private const int PrepopulateBindTimeout = 60000;
#region Instance fields and constructor

public const string SQL_LANG = "SQL";

public const string SQL_CMD_LANG = "SQLCMD";

private const int OneSecond = 1000;

private const int PrepopulateBindTimeout = 60000;

internal const string DefaultBatchSeperator = "GO";

internal const int DiagnosticParseDelay = 750;
Expand All @@ -80,6 +84,9 @@ public static LanguageService Instance

internal const int CompletionExtTimeout = 200;

// For testability only
internal Task DelayedDiagnosticsTask = null;

private ConnectionService connectionService = null;

private WorkspaceService<SqlToolsSettings> workspaceServiceInstance;
Expand Down Expand Up @@ -836,7 +843,10 @@ public async Task HandleDidChangeTextDocumentNotification(ScriptFile[] changedFi
if (SQL_LANG.Equals(changeParams.Language, StringComparison.OrdinalIgnoreCase)) {
shouldBlock = !ServiceHost.ProviderName.Equals(changeParams.Flavor, StringComparison.OrdinalIgnoreCase);
}

if (SQL_CMD_LANG.Equals(changeParams.Language, StringComparison.OrdinalIgnoreCase))
{
shouldBlock = true; // the provider will continue to be mssql
}
if (shouldBlock) {
this.nonMssqlUriMap.AddOrUpdate(changeParams.Uri, true, (k, oldValue) => true);
if (CurrentWorkspace.ContainsFile(changeParams.Uri))
Expand All @@ -848,7 +858,10 @@ public async Task HandleDidChangeTextDocumentNotification(ScriptFile[] changedFi
{
bool value;
this.nonMssqlUriMap.TryRemove(changeParams.Uri, out value);
}
// should rebuild intellisense when re-considering as sql
RebuildIntelliSenseParams param = new RebuildIntelliSenseParams { OwnerUri = changeParams.Uri };
await HandleRebuildIntelliSenseNotification(param, eventContext);
}
}
catch (Exception ex)
{
Expand Down Expand Up @@ -1733,7 +1746,7 @@ private Task RunScriptDiagnostics(ScriptFile[] filesToAnalyze, EventContext even
existingRequestCancellation = new CancellationTokenSource();
Task.Factory.StartNew(
() =>
DelayThenInvokeDiagnostics(
this.DelayedDiagnosticsTask = DelayThenInvokeDiagnostics(
LanguageService.DiagnosticParseDelay,
filesToAnalyze,
eventContext,
Expand Down
8 changes: 7 additions & 1 deletion src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,13 @@ public class Query : IDisposable

// Process the query into batches
BatchParserWrapper parser = new BatchParserWrapper();
List<BatchDefinition> parserResult = parser.GetBatches(queryText);
ExecutionEngineConditions conditions = null;
if (settings.IsSqlCmdMode)
{
conditions = new ExecutionEngineConditions();
conditions.IsSqlCmd = settings.IsSqlCmdMode;
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
}
List<BatchDefinition> parserResult = parser.GetBatches(queryText, conditions);
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved

var batchSelection = parserResult
.Select((batchDefinition, index) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ public class QueryExecutionSettings : GeneralRequestDetails
/// </summary>
private const int DefaultQueryGovernorCostLimit = 0;

/// <summary>
/// Flag to run query in sqlcmd mode
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
private bool DefaultSqlCmdMode = false;

#endregion

#region Member Variables
Expand Down Expand Up @@ -633,6 +638,21 @@ public bool IncludeEstimatedExecutionPlanXml
}
}

/// <summary>
/// Set sqlCmd Mode
/// </summary>
public bool IsSqlCmdMode
{
get
{
return GetOptionValue<bool>("isSqlCmdMode", DefaultSqlCmdMode);
}
set
{
SetOptionValue("isSqlCmdMode", value);
}
}

#endregion

#region Public Methods
Expand Down Expand Up @@ -670,6 +690,7 @@ public void Update(QueryExecutionSettings newSettings)
AnsiPadding = newSettings.AnsiPadding;
AnsiWarnings = newSettings.AnsiWarnings;
AnsiNulls = newSettings.AnsiNulls;
IsSqlCmdMode = newSettings.IsSqlCmdMode;
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,41 @@ public void VerifyIsSqlCmd()
}
}

// Verify whether the batchParser execute SqlCmd successfully
[Fact]
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
public void VerifyRunSqlCmd()
{
using (ExecutionEngine executionEngine = new ExecutionEngine())
{
string sqlCmdQuery = @"
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
:setvar __var1 1
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
:setvar __var2 2
:setvar __IsSqlCmdEnabled " + "\"True\"" + @"
GO
IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True'
BEGIN
PRINT N'SQLCMD mode must be enabled to successfully execute this script.';
SET NOEXEC ON;
END
GO
select $(__var1) + $(__var2) as col
GO";

using (SqlConnection con = new SqlConnection(CONNECTION_STRING))
{
con.Open();
var condition = new ExecutionEngineConditions();
condition.IsSqlCmd = true;
TestExecutor testExecutor = new TestExecutor(sqlCmdQuery, con, condition);
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
testExecutor.Run();

Assert.True(testExecutor.ResultCountQueue.Count >= 1);
Assert.True(testExecutor.ErrorMessageQueue.Count == 0);

}
}
}

// Verify whether the executionEngine execute Batch
[Fact]
public void VerifyExecuteBatch()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public TestExecutor(string batch, SqlConnection conn, ExecutionEngineConditions
conditions.IsNoExec = exeCondition.IsNoExec;
conditions.IsStatisticsIO = exeCondition.IsStatisticsIO;
conditions.IsStatisticsTime = exeCondition.IsStatisticsTime;
conditions.IsSqlCmd = exeCondition.IsSqlCmd;

_cancel = cancelExecution;
connection = conn;
Expand Down
Loading