Adding new setting for numeric or text bit display (#243)

Adding a new setting to query execution setting that will change the display value we generate for `BIT` columns. The new setting is `DefaultDisplayBitAsNumber`. If true, bit columns will be displayed as 1 or 0. If false, they'll be displayed as true or false. The default value is true, to keep parity with SSMS behavior.

Enables us to solve https://github.com/Microsoft/vscode-mssql/issues/690 and https://github.com/Microsoft/vscode-mssql/issues/513
This commit is contained in:
Benjamin Russell
2017-02-21 19:29:55 -08:00
committed by GitHub
parent ccd2c9caa9
commit 55a56be316
14 changed files with 199 additions and 113 deletions

View File

@@ -58,7 +58,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <summary>
/// Special action which this batch performed
/// </summary>
private SpecialAction specialAction;
private readonly SpecialAction specialAction;
#endregion
@@ -128,7 +128,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// Localized timestamp for when the execution completed.
/// Stored in UTC ISO 8601 format; should be localized before displaying to any user
/// </summary>
public string ExecutionEndTimeStamp { get { return executionEndTime.ToString("o"); } }
public string ExecutionEndTimeStamp => executionEndTime.ToString("o");
/// <summary>
/// Localized timestamp for how long it took for the execution to complete
@@ -146,7 +146,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// Localized timestamp for when the execution began.
/// Stored in UTC ISO 8601 format; should be localized before displaying to any user
/// </summary>
public string ExecutionStartTimeStamp { get { return executionStartTime.ToString("o"); } }
public string ExecutionStartTimeStamp => executionStartTime.ToString("o");
/// <summary>
/// Whether or not this batch encountered an error that halted execution
@@ -161,15 +161,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <summary>
/// Ordinal of the batch in the query
/// </summary>
public int Id { get; private set; }
public int Id { get; }
/// <summary>
/// The result sets of the batch execution
/// </summary>
public IList<ResultSet> ResultSets
{
get { return resultSets; }
}
public IList<ResultSet> ResultSets => resultSets;
/// <summary>
/// Property for generating a set result set summaries from the result sets
@@ -381,7 +378,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// Generates an execution plan
/// </summary>
/// <param name="resultSetIndex">The index for selecting the result set</param>
/// <returns>An exeuction plan object</returns>
/// <returns>An execution plan object</returns>
public Task<ExecutionPlan> GetExecutionPlan(int resultSetIndex)
{
ResultSet targetResultSet;

View File

@@ -6,6 +6,7 @@
using System;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
@@ -17,6 +18,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
#region Properties
/// <summary>
/// Settings for query execution
/// </summary>
public QueryExecutionSettings QueryExecutionSettings { get; set; }
/// <summary>
/// Parameters for the save as CSV request
/// </summary>
@@ -41,7 +47,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>Stream reader</returns>
public IFileStreamReader GetReader(string fileName)
{
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read));
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read), QueryExecutionSettings);
}
/// <summary>

View File

@@ -6,6 +6,7 @@
using System;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
@@ -14,6 +15,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#region Properties
/// <summary>
/// Settings for query execution
/// </summary>
public QueryExecutionSettings QueryExecutionSettings { get; set; }
/// <summary>
/// Parameters for the save as JSON request
/// </summary>
@@ -28,7 +34,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
[Obsolete]
public string CreateFile()
{
throw new NotImplementedException();
throw new InvalidOperationException();
}
/// <summary>
@@ -38,7 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>Stream reader</returns>
public IFileStreamReader GetReader(string fileName)
{
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read));
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read), QueryExecutionSettings);
}
/// <summary>

View File

@@ -4,6 +4,7 @@
//
using System.IO;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
@@ -15,14 +16,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#region Properties
/// <summary>
/// The maximum number of characters to store from long text fields
/// The settings for query execution
/// </summary>
public int MaxCharsToStore { get; set; }
/// <summary>
/// The maximum number of characters to store from xml fields
/// </summary>
public int MaxXmlCharsToStore { get; set; }
public QueryExecutionSettings ExecutionSettings { get; set; }
#endregion
@@ -43,7 +39,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>A <see cref="ServiceBufferFileStreamReader"/></returns>
public IFileStreamReader GetReader(string fileName)
{
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read));
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read), ExecutionSettings);
}
/// <summary>
@@ -54,7 +50,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>A <see cref="ServiceBufferFileStreamWriter"/></returns>
public IFileStreamWriter GetWriter(string fileName)
{
return new ServiceBufferFileStreamWriter(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite), MaxCharsToStore, MaxXmlCharsToStore);
return new ServiceBufferFileStreamWriter(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite), ExecutionSettings);
}
/// <summary>

View File

@@ -9,6 +9,8 @@ using System.Data.SqlTypes;
using System.IO;
using System.Text;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
@@ -30,6 +32,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
private byte[] buffer;
private readonly QueryExecutionSettings executionSettings;
private readonly Stream fileStream;
private readonly Dictionary<Type, Func<long, DbColumnWrapper, FileStreamReadResult>> readMethods;
@@ -40,8 +44,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Constructs a new ServiceBufferFileStreamReader and initializes its state
/// </summary>
/// <param name="stream">The filestream to read from</param>
public ServiceBufferFileStreamReader(Stream stream)
{
/// <param name="settings">The query execution settings</param>
public ServiceBufferFileStreamReader(Stream stream, QueryExecutionSettings settings)
{
Validate.IsNotNull(nameof(stream), stream);
Validate.IsNotNull(nameof(settings), settings);
// Open file for reading/writing
if (!stream.CanRead || !stream.CanSeek)
{
@@ -49,6 +57,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
}
fileStream = stream;
executionSettings = settings;
// Create internal buffer
buffer = new byte[DefaultBufferSize];
@@ -259,7 +269,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>A bool</returns>
internal FileStreamReadResult ReadBoolean(long fileOffset)
{
return ReadCellHelper(fileOffset, length => buffer[0] == 0x1);
// Override the stringifier with numeric values if the user prefers that
return ReadCellHelper(fileOffset, length => buffer[0] == 0x1,
toStringFunc: val => executionSettings.DisplayBitAsNumber
? val ? "1" : "0"
: val.ToString());
}
/// <summary>
@@ -505,10 +519,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <summary>
/// <see cref="LengthLength"/> + <see cref="ValueLength"/>
/// </summary>
public int TotalLength
{
get { return LengthLength + ValueLength; }
}
public int TotalLength => LengthLength + ValueLength;
}
#region IDisposable Implementation

View File

@@ -10,6 +10,7 @@ using System.Diagnostics;
using System.IO;
using System.Text;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
@@ -24,8 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#region Member Variables
private readonly Stream fileStream;
private readonly int maxCharsToStore;
private readonly int maxXmlCharsToStore;
private readonly QueryExecutionSettings executionSettings;
private byte[] byteBuffer;
private readonly short[] shortBuffer;
@@ -46,16 +46,19 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Constructs a new writer
/// </summary>
/// <param name="stream">The file wrapper to use as the underlying file stream</param>
/// <param name="maxCharsToStore">Maximum number of characters to store for long text fields</param>
/// <param name="maxXmlCharsToStore">Maximum number of characters to store for XML fields</param>
public ServiceBufferFileStreamWriter(Stream stream, int maxCharsToStore, int maxXmlCharsToStore)
/// <param name="settings">The query execution settings</param>
public ServiceBufferFileStreamWriter(Stream stream, QueryExecutionSettings settings)
{
Validate.IsNotNull(nameof(stream), stream);
Validate.IsNotNull(nameof(settings), settings);
// open file for reading/writing
if (!stream.CanWrite || !stream.CanSeek)
{
throw new InvalidOperationException("Stream must be writable and seekable.");
}
fileStream = stream;
executionSettings = settings;
// create internal buffer
byteBuffer = new byte[DefaultBufferLength];
@@ -69,10 +72,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
doubleBuffer = new double[1];
floatBuffer = new float[1];
// Store max chars to store
this.maxCharsToStore = maxCharsToStore;
this.maxXmlCharsToStore = maxXmlCharsToStore;
// Define what methods to use to write a type to the file
writeMethods = new Dictionary<Type, Func<object, int>>
{
@@ -145,18 +144,18 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// this is a long field
if (ci.IsBytes)
{
values[i] = reader.GetBytesWithMaxCapacity(i, maxCharsToStore);
values[i] = reader.GetBytesWithMaxCapacity(i, executionSettings.MaxCharsToStore);
}
else if (ci.IsChars)
{
Debug.Assert(maxCharsToStore > 0);
values[i] = reader.GetCharsWithMaxCapacity(i,
ci.IsXml ? maxXmlCharsToStore : maxCharsToStore);
int maxChars = ci.IsXml
? executionSettings.MaxXmlCharsToStore
: executionSettings.MaxCharsToStore;
values[i] = reader.GetCharsWithMaxCapacity(i, maxChars);
}
else if (ci.IsXml)
{
Debug.Assert(maxXmlCharsToStore > 0);
values[i] = reader.GetXmlWithMaxCapacity(i, maxXmlCharsToStore);
values[i] = reader.GetXmlWithMaxCapacity(i, executionSettings.MaxXmlCharsToStore);
}
else
{

View File

@@ -53,16 +53,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// </summary>
private bool hasExecuteBeenCalled;
/// <summary>
/// Settings for query runtime
/// </summary>
private QueryExecutionSettings querySettings;
/// <summary>
/// Streaming output factory for the query
/// </summary>
private IFileStreamFactory streamOutputFactory;
/// <summary>
/// Name of the new database if the database name was changed in the query
/// </summary>
@@ -109,8 +99,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
QueryText = queryText;
editorConnection = connection;
cancellationSource = new CancellationTokenSource();
querySettings = settings;
streamOutputFactory = outputFactory;
// Process the query into batches
BatchParserWrapper parser = new BatchParserWrapper();
@@ -135,16 +123,16 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
if (DoesSupportExecutionPlan(connection))
{
// Checking settings for execution plan options
if (querySettings.ExecutionPlanOptions.IncludeEstimatedExecutionPlanXml)
if (settings.ExecutionPlanOptions.IncludeEstimatedExecutionPlanXml)
{
// Enable set showplan xml
addBatch(string.Format(SetShowPlanXml, On), BeforeBatches, streamOutputFactory);
addBatch(string.Format(SetShowPlanXml, Off), AfterBatches, streamOutputFactory);
AddBatch(string.Format(SetShowPlanXml, On), BeforeBatches, outputFactory);
AddBatch(string.Format(SetShowPlanXml, Off), AfterBatches, outputFactory);
}
else if (querySettings.ExecutionPlanOptions.IncludeActualExecutionPlanXml)
else if (settings.ExecutionPlanOptions.IncludeActualExecutionPlanXml)
{
addBatch(string.Format(SetStatisticsXml, On), BeforeBatches, streamOutputFactory);
addBatch(string.Format(SetStatisticsXml, Off), AfterBatches, streamOutputFactory);
AddBatch(string.Format(SetStatisticsXml, On), BeforeBatches, outputFactory);
AddBatch(string.Format(SetStatisticsXml, Off), AfterBatches, outputFactory);
}
}
}
@@ -182,11 +170,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// </summary>
public event QueryAsyncEventHandler QueryFailed;
/// <summary>
/// Callback for when the query connection has failed
/// </summary>
public event QueryAsyncErrorEventHandler QueryConnectionException;
/// <summary>
/// Event to be called when a resultset has completed.
/// </summary>
@@ -459,7 +442,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <summary>
/// Function to add a new batch to a Batch set
/// </summary>
private void addBatch(string query, List<Batch> batchSet, IFileStreamFactory outputFactory)
private static void AddBatch(string query, ICollection<Batch> batchSet, IFileStreamFactory outputFactory)
{
batchSet.Add(new Batch(query, null, batchSet.Count, outputFactory));
}

View File

@@ -64,11 +64,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
get
{
return BufferFileStreamFactory ?? (BufferFileStreamFactory = new ServiceBufferFileStreamFactory
if (BufferFileStreamFactory == null)
{
MaxCharsToStore = Settings.SqlTools.QueryExecutionSettings.MaxCharsToStore,
MaxXmlCharsToStore = Settings.SqlTools.QueryExecutionSettings.MaxXmlCharsToStore
});
BufferFileStreamFactory = new ServiceBufferFileStreamFactory
{
ExecutionSettings = Settings.QueryExecutionSettings
};
}
return BufferFileStreamFactory;
}
}
@@ -295,7 +298,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Use the default CSV file factory if we haven't overridden it
IFileStreamFactory csvFactory = CsvFileFactory ?? new SaveAsCsvFileStreamFactory
{
SaveRequestParams = saveParams
SaveRequestParams = saveParams,
QueryExecutionSettings = Settings.QueryExecutionSettings
};
await SaveResultsHelper(saveParams, requestContext, csvFactory);
}
@@ -309,7 +313,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Use the default JSON file factory if we haven't overridden it
IFileStreamFactory jsonFactory = JsonFileFactory ?? new SaveAsJsonFileStreamFactory
{
SaveRequestParams = saveParams
SaveRequestParams = saveParams,
QueryExecutionSettings = Settings.QueryExecutionSettings
};
await SaveResultsHelper(saveParams, requestContext, jsonFactory);
}
@@ -452,20 +457,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
await eventSender.SendEvent(QueryCompleteEvent.Type, eventParams);
};
Query.QueryAsyncErrorEventHandler errorCallback = async errorMessage =>
{
// Send back the error message
QueryCompleteParams eventParams = new QueryCompleteParams
{
OwnerUri = ownerUri,
//Message = errorMessage
};
await eventSender.SendEvent(QueryCompleteEvent.Type, eventParams);
};
query.QueryCompleted += callback;
query.QueryFailed += callback;
query.QueryConnectionException += errorCallback;
// Setup the batch callbacks
Batch.BatchAsyncEventHandler batchStartCallback = async b =>

View File

@@ -25,8 +25,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
#region Constants
// Column names of 'for xml' and 'for json' queries
private const string NameOfForXMLColumn = "XML_F52E2B61-18A1-11d1-B105-00805F49916B";
private const string NameOfForJSONColumn = "JSON_F52E2B61-18A1-11d1-B105-00805F49916B";
private const string NameOfForXmlColumn = "XML_F52E2B61-18A1-11d1-B105-00805F49916B";
private const string NameOfForJsonColumn = "JSON_F52E2B61-18A1-11d1-B105-00805F49916B";
private const string YukonXmlShowPlanColumn = "Microsoft SQL Server 2005 XML Showplan";
#endregion
@@ -72,7 +72,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <summary>
/// The special action which applied to this result set
/// </summary>
private SpecialAction specialAction;
private readonly SpecialAction specialAction;
#endregion
@@ -251,7 +251,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// <returns>An execution plan object</returns>
public Task<ExecutionPlan> GetExecutionPlan()
{
// Proccess the action just incase is hasn't been yet
// Process the action just incase is hasn't been yet
ProcessSpecialAction();
// Sanity check to make sure that the results have been read beforehand
@@ -260,7 +260,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
throw new InvalidOperationException(SR.QueryServiceResultSetNotRead);
}
// Check that we this result set contains a showplan
else if (!specialAction.ExpectYukonXMLShowPlan)
if (!specialAction.ExpectYukonXMLShowPlan)
{
throw new Exception(SR.QueryServiceExecutionPlanNotFound);
}
@@ -268,7 +268,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
return Task.Factory.StartNew(() =>
{
string content = null;
string content;
string format = null;
using (IFileStreamReader fileStreamReader = fileStreamFactory.GetReader(outputFileName))
@@ -333,6 +333,15 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
}
/// <summary>
/// Saves the contents of this result set to a file using the IFileStreamFactory provided
/// </summary>
/// <param name="saveParams">Parameters for saving the results to a file</param>
/// <param name="fileFactory">
/// Factory for creating a stream reader/writer combo for writing results to disk
/// </param>
/// <param name="successHandler">Handler for a successful write of all rows</param>
/// <param name="failureHandler">Handler for unsuccessful write of all rows</param>
public void SaveAs(SaveResultsRequestParams saveParams, IFileStreamFactory fileFactory,
SaveAsAsyncEventHandler successHandler, SaveAsFailureAsyncEventHandler failureHandler)
{
@@ -475,13 +484,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
if (Columns?.Length == 1 && RowCount != 0)
{
if (Columns[0].ColumnName.Equals(NameOfForXMLColumn, StringComparison.Ordinal))
if (Columns[0].ColumnName.Equals(NameOfForXmlColumn, StringComparison.Ordinal))
{
Columns[0].IsXml = true;
isSingleColumnXmlJsonResultSet = true;
RowCount = 1;
}
else if (Columns[0].ColumnName.Equals(NameOfForJSONColumn, StringComparison.Ordinal))
else if (Columns[0].ColumnName.Equals(NameOfForJsonColumn, StringComparison.Ordinal))
{
Columns[0].IsJson = true;
isSingleColumnXmlJsonResultSet = true;