diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamFactory.cs
index a0fd2251..2e59f15b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamFactory.cs
@@ -3,6 +3,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+using System.Collections.Generic;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
+
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
///
@@ -14,7 +17,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
IFileStreamReader GetReader(string fileName);
- IFileStreamWriter GetWriter(string fileName);
+ IFileStreamWriter GetWriter(string fileName, IReadOnlyList columns = null);
void DisposeFile(string fileName);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs
index d46d9e6f..51399534 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/IFileStreamWriter.cs
@@ -16,7 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
public interface IFileStreamWriter : IDisposable
{
int WriteRow(StorageDataReader dataReader);
- void WriteRow(IList row, IList columns);
+ void WriteRow(IList row, IReadOnlyList columns);
void Seek(long offset);
void FlushBuffer();
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamFactory.cs
index 75634de2..92499db7 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamFactory.cs
@@ -1,9 +1,10 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
+using System.Collections.Generic;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -48,17 +49,28 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Stream reader
public IFileStreamReader GetReader(string fileName)
{
- return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), QueryExecutionSettings);
+ return new ServiceBufferFileStreamReader(
+ new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
+ QueryExecutionSettings
+ );
}
///
/// Returns a new CSV writer for writing results to a CSV file, file share is ReadWrite to allow concurrent reads/writes to the file.
///
/// Path to the CSV output file
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
/// Stream writer
- public IFileStreamWriter GetWriter(string fileName)
+ public IFileStreamWriter GetWriter(string fileName, IReadOnlyList columns)
{
- return new SaveAsCsvFileStreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite), SaveRequestParams);
+ return new SaveAsCsvFileStreamWriter(
+ new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite),
+ SaveRequestParams,
+ columns
+ );
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamWriter.cs
index 8f962267..7e302392 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsCsvFileStreamWriter.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
@@ -20,21 +20,74 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#region Member Variables
- private readonly SaveResultsAsCsvRequestParams saveParams;
- private bool headerWritten;
+ private readonly char delimiter;
+ private readonly Encoding encoding;
+ private readonly string lineSeparator;
+ private readonly char textIdentifier;
+ private readonly string textIdentifierString;
#endregion
///
- /// Constructor, stores the CSV specific request params locally, chains into the base
+ /// Constructor, stores the CSV specific request params locally, chains into the base
/// constructor
///
/// FileStream to access the CSV file output
/// CSV save as request parameters
- public SaveAsCsvFileStreamWriter(Stream stream, SaveResultsAsCsvRequestParams requestParams)
- : base(stream, requestParams)
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
+ public SaveAsCsvFileStreamWriter(Stream stream, SaveResultsAsCsvRequestParams requestParams, IReadOnlyList columns)
+ : base(stream, requestParams, columns)
{
- saveParams = requestParams;
+ // Parse the config
+ delimiter = ',';
+ if (!string.IsNullOrEmpty(requestParams.Delimiter))
+ {
+ delimiter = requestParams.Delimiter[0];
+ }
+
+ lineSeparator = Environment.NewLine;
+ if (!string.IsNullOrEmpty(requestParams.LineSeperator))
+ {
+ lineSeparator = requestParams.LineSeperator;
+ }
+
+ textIdentifier = '"';
+ if (!string.IsNullOrEmpty(requestParams.TextIdentifier))
+ {
+ textIdentifier = requestParams.TextIdentifier[0];
+ }
+ textIdentifierString = textIdentifier.ToString();
+
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+ try
+ {
+ encoding = int.TryParse(requestParams.Encoding, out int codePage)
+ ? Encoding.GetEncoding(codePage)
+ : Encoding.GetEncoding(requestParams.Encoding);
+ }
+ catch
+ {
+ // Fallback encoding when specified codepage is invalid
+ encoding = Encoding.GetEncoding("utf-8");
+ }
+
+ // Output the header if the user requested it
+ if (requestParams.IncludeHeaders)
+ {
+ // Build the string
+ var selectedColumns = columns.Skip(ColumnStartIndex)
+ .Take(ColumnCount)
+ .Select(c => EncodeCsvField(c.ColumnName) ?? string.Empty);
+
+ string headerLine = string.Join(delimiter, selectedColumns);
+
+ // Encode it and write it out
+ byte[] headerBytes = encoding.GetBytes(headerLine + lineSeparator);
+ FileStream.Write(headerBytes, 0, headerBytes.Length);
+ }
}
///
@@ -42,76 +95,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// it, the headers for the column will be emitted as well.
///
/// The data of the row to output to the file
- ///
- /// The entire list of columns for the result set. They will be filtered down as per the
- /// request params.
- ///
- public override void WriteRow(IList row, IList columns)
+ /// The columns for the row to output
+ public override void WriteRow(IList row, IReadOnlyList columns)
{
- char delimiter = ',';
- if(!string.IsNullOrEmpty(saveParams.Delimiter))
- {
- // first char in string
- delimiter = saveParams.Delimiter[0];
- }
-
- string lineSeperator = Environment.NewLine;
- if(!string.IsNullOrEmpty(saveParams.LineSeperator))
- {
- lineSeperator = saveParams.LineSeperator;
- }
-
- char textIdentifier = '"';
- if(!string.IsNullOrEmpty(saveParams.TextIdentifier))
- {
- // first char in string
- textIdentifier = saveParams.TextIdentifier[0];
- }
-
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- int codepage;
- Encoding encoding;
- try
- {
- if(int.TryParse(saveParams.Encoding, out codepage))
- {
- encoding = Encoding.GetEncoding(codepage);
- }
- else
- {
- encoding = Encoding.GetEncoding(saveParams.Encoding);
- }
- }
- catch
- {
- // Fallback encoding when specified codepage is invalid
- encoding = Encoding.GetEncoding("utf-8");
- }
-
- // Write out the header if we haven't already and the user chose to have it
- if (saveParams.IncludeHeaders && !headerWritten)
- {
- // Build the string
- var selectedColumns = columns.Skip(ColumnStartIndex ?? 0).Take(ColumnCount ?? columns.Count)
- .Select(c => EncodeCsvField(c.ColumnName, delimiter, textIdentifier) ?? string.Empty);
-
- string headerLine = string.Join(delimiter, selectedColumns);
-
- // Encode it and write it out
- byte[] headerBytes = encoding.GetBytes(headerLine + lineSeperator);
- FileStream.Write(headerBytes, 0, headerBytes.Length);
-
- headerWritten = true;
- }
-
// Build the string for the row
- var selectedCells = row.Skip(ColumnStartIndex ?? 0)
- .Take(ColumnCount ?? columns.Count)
- .Select(c => EncodeCsvField(c.DisplayValue, delimiter, textIdentifier));
+ var selectedCells = row.Skip(ColumnStartIndex)
+ .Take(ColumnCount)
+ .Select(c => EncodeCsvField(c.DisplayValue));
string rowLine = string.Join(delimiter, selectedCells);
// Encode it and write it out
- byte[] rowBytes = encoding.GetBytes(rowLine + lineSeperator);
+ byte[] rowBytes = encoding.GetBytes(rowLine + lineSeparator);
FileStream.Write(rowBytes, 0, rowBytes.Length);
}
@@ -124,7 +118,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// The field begins or ends with a space
/// The field begins or ends with a tab
- /// The field contains the ListSeparator string
+ /// The field contains the delimiter string
/// The field contains the '\n' character
/// The field contains the '\r' character
/// The field contains the '"' character
@@ -132,27 +126,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// The field to encode
/// The CSV encoded version of the original field
- internal static string EncodeCsvField(string field, char delimiter, char textIdentifier)
+ internal string EncodeCsvField(string field)
{
- string strTextIdentifier = textIdentifier.ToString();
-
// Special case for nulls
if (field == null)
{
return "NULL";
}
+ // Replace all quotes in the original field with double quotes
+ string ret = field.Replace(textIdentifierString, textIdentifierString + textIdentifierString);
+
// Whether this field has special characters which require it to be embedded in quotes
bool embedInQuotes = field.IndexOfAny(new[] { delimiter, '\r', '\n', textIdentifier }) >= 0 // Contains special characters
|| field.StartsWith(" ") || field.EndsWith(" ") // Start/Ends with space
|| field.StartsWith("\t") || field.EndsWith("\t"); // Starts/Ends with tab
-
- //Replace all quotes in the original field with double quotes
- string ret = field.Replace(strTextIdentifier, strTextIdentifier + strTextIdentifier);
-
if (embedInQuotes)
{
- ret = strTextIdentifier + $"{ret}" + strTextIdentifier;
+ ret = $"{textIdentifier}{ret}{textIdentifier}";
}
return ret;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamFactory.cs
index 29f514bf..f25fea98 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamFactory.cs
@@ -1,9 +1,10 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
+using System.Collections.Generic;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -48,17 +49,28 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Stream reader
public IFileStreamReader GetReader(string fileName)
{
- return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), QueryExecutionSettings);
+ return new ServiceBufferFileStreamReader(
+ new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
+ QueryExecutionSettings
+ );
}
///
/// Returns a new Excel writer for writing results to a Excel file, file share is ReadWrite to allow concurrent reads/writes to the file.
///
/// Path to the Excel output file
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
/// Stream writer
- public IFileStreamWriter GetWriter(string fileName)
+ public IFileStreamWriter GetWriter(string fileName, IReadOnlyList columns)
{
- return new SaveAsExcelFileStreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite), SaveRequestParams);
+ return new SaveAsExcelFileStreamWriter(
+ new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite),
+ SaveRequestParams,
+ columns
+ );
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamWriter.cs
index 1381e91e..7e1e9833 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsExcelFileStreamWriter.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
@@ -25,13 +25,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#endregion
///
- /// Constructor, stores the Excel specific request params locally, chains into the base
+ /// Constructor, stores the Excel specific request params locally, chains into the base
/// constructor
///
/// FileStream to access the Excel file output
/// Excel save as request parameters
- public SaveAsExcelFileStreamWriter(Stream stream, SaveResultsAsExcelRequestParams requestParams)
- : base(stream, requestParams)
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
+ public SaveAsExcelFileStreamWriter(Stream stream, SaveResultsAsExcelRequestParams requestParams, IReadOnlyList columns)
+ : base(stream, requestParams, columns)
{
saveParams = requestParams;
helper = new SaveAsExcelFileStreamWriterHelper(stream);
@@ -47,16 +51,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// The entire list of columns for the result set. They will be filtered down as per the
/// request params.
///
- public override void WriteRow(IList row, IList columns)
+ public override void WriteRow(IList row, IReadOnlyList columns)
{
- int columnStart = ColumnStartIndex ?? 0;
- int columnEnd = (ColumnEndIndex != null) ? ColumnEndIndex.Value + 1 : columns.Count;
-
// Write out the header if we haven't already and the user chose to have it
if (saveParams.IncludeHeaders && !headerWritten)
{
sheet.AddRow();
- for (int i = columnStart; i < columnEnd; i++)
+ for (int i = ColumnStartIndex; i <= ColumnEndIndex; i++)
{
sheet.AddCell(columns[i].ColumnName);
}
@@ -64,7 +65,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
}
sheet.AddRow();
- for (int i = columnStart; i < columnEnd; i++)
+ for (int i = ColumnStartIndex; i <= ColumnEndIndex; i++)
{
sheet.AddCell(row[i]);
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamFactory.cs
index 7e09f68d..cb96e0e2 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamFactory.cs
@@ -1,9 +1,10 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
+using System.Collections.Generic;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -52,10 +53,18 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Returns a new JSON writer for writing results to a JSON file, file share is ReadWrite to allow concurrent reads/writes to the file.
///
/// Path to the JSON output file
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
/// Stream writer
- public IFileStreamWriter GetWriter(string fileName)
+ public IFileStreamWriter GetWriter(string fileName, IReadOnlyList columns)
{
- return new SaveAsJsonFileStreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite), SaveRequestParams);
+ return new SaveAsJsonFileStreamWriter(
+ new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite),
+ SaveRequestParams,
+ columns
+ );
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamWriter.cs
index e232b1b6..9eb302e8 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsJsonFileStreamWriter.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
@@ -33,8 +33,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// FileStream to access the JSON file output
/// JSON save as request parameters
- public SaveAsJsonFileStreamWriter(Stream stream, SaveResultsRequestParams requestParams)
- : base(stream, requestParams)
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
+ public SaveAsJsonFileStreamWriter(Stream stream, SaveResultsRequestParams requestParams, IReadOnlyList columns)
+ : base(stream, requestParams, columns)
{
// Setup the internal state
streamWriter = new StreamWriter(stream);
@@ -53,15 +57,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// The entire list of columns for the result set. They will be filtered down as per the
/// request params.
///
- public override void WriteRow(IList row, IList columns)
+ public override void WriteRow(IList row, IReadOnlyList columns)
{
// Write the header for the object
jsonWriter.WriteStartObject();
// Write the items out as properties
- int columnStart = ColumnStartIndex ?? 0;
- int columnEnd = (ColumnEndIndex != null) ? ColumnEndIndex.Value + 1 : columns.Count;
- for (int i = columnStart; i < columnEnd; i++)
+ for (int i = ColumnStartIndex; i <= ColumnEndIndex; i++)
{
jsonWriter.WritePropertyName(columns[i].ColumnName);
if (row[i].RawObject == null)
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs
index 12ff6b46..9fa71d7d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
+using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
@@ -21,18 +22,31 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// The stream that will be written to
/// The SaveAs request parameters
- protected SaveAsStreamWriter(Stream stream, SaveResultsRequestParams requestParams)
+ ///
+ /// The entire list of columns for the result set. Used to determine which columns to
+ /// output.
+ ///
+ protected SaveAsStreamWriter(Stream stream, SaveResultsRequestParams requestParams, IReadOnlyList columns)
{
+ Validate.IsNotNull(nameof(stream), stream);
+ Validate.IsNotNull(nameof(columns), columns);
+
FileStream = stream;
- var saveParams = requestParams;
if (requestParams.IsSaveSelection)
{
// ReSharper disable PossibleInvalidOperationException IsSaveSelection verifies these values exist
- ColumnStartIndex = saveParams.ColumnStartIndex.Value;
- ColumnEndIndex = saveParams.ColumnEndIndex.Value;
- ColumnCount = saveParams.ColumnEndIndex.Value - saveParams.ColumnStartIndex.Value + 1;
+ ColumnStartIndex = requestParams.ColumnStartIndex.Value;
+ ColumnEndIndex = requestParams.ColumnEndIndex.Value;
// ReSharper restore PossibleInvalidOperationException
}
+ else
+ {
+ // Save request was for the entire result set, use default start/end
+ ColumnStartIndex = 0;
+ ColumnEndIndex = columns.Count - 1;
+ }
+
+ ColumnCount = ColumnEndIndex - ColumnStartIndex + 1;
}
#region Properties
@@ -40,22 +54,22 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// Index of the first column to write to the output file
///
- protected int? ColumnStartIndex { get; private set; }
+ protected int ColumnStartIndex { get; }
///
/// Number of columns to write to the output file
///
- protected int? ColumnCount { get; private set; }
+ protected int ColumnCount { get; }
///
- /// Index of the last column to write to the output file
+ /// Index of the last column to write to the output file (inclusive).
///
- protected int? ColumnEndIndex { get; private set; }
+ protected int ColumnEndIndex { get; }
///
/// The file stream to use to write the output file
///
- protected Stream FileStream { get; private set; }
+ protected Stream FileStream { get; }
#endregion
@@ -73,7 +87,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// The row of data to output
/// The list of columns to output
- public abstract void WriteRow(IList row, IList columns);
+ public abstract void WriteRow(IList row, IReadOnlyList columns);
///
/// Not implemented, do not use.
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamFactory.cs
index e8f23f89..e6d85155 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamFactory.cs
@@ -1,9 +1,10 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
+using System.Collections.Generic;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -45,17 +46,28 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// Stream reader
public IFileStreamReader GetReader(string fileName)
{
- return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), QueryExecutionSettings);
+ return new ServiceBufferFileStreamReader(
+ new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
+ QueryExecutionSettings
+ );
}
///
/// Returns a new XML writer for writing results to a XML file, file share is ReadWrite to allow concurrent reads/writes to the file.
///
/// Path to the XML output file
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
/// Stream writer
- public IFileStreamWriter GetWriter(string fileName)
+ public IFileStreamWriter GetWriter(string fileName, IReadOnlyList columns)
{
- return new SaveAsXmlFileStreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite), SaveRequestParams);
+ return new SaveAsXmlFileStreamWriter(
+ new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite),
+ SaveRequestParams,
+ columns
+ );
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamWriter.cs
index b772a9a8..0698525e 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsXmlFileStreamWriter.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
@@ -24,10 +24,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
// Root element name for the output XML
private const string RootElementTag = "data";
-
+
// Item element name which will be used for every row
private const string ItemElementTag = "row";
-
+
#region Member Variables
private readonly XmlTextWriter xmlTextWriter;
@@ -39,8 +39,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// FileStream to access the JSON file output
/// XML save as request parameters
- public SaveAsXmlFileStreamWriter(Stream stream, SaveResultsAsXmlRequestParams requestParams)
- : base(stream, requestParams)
+ ///
+ /// The entire list of columns for the result set. They will be filtered down as per the
+ /// request params.
+ ///
+ public SaveAsXmlFileStreamWriter(Stream stream, SaveResultsAsXmlRequestParams requestParams, IReadOnlyList columns)
+ : base(stream, requestParams, columns)
{
// Setup the internal state
var encoding = GetEncoding(requestParams);
@@ -60,19 +64,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// The entire list of columns for the result set. They will be filtered down as per the
/// request params.
///
- public override void WriteRow(IList row, IList columns)
+ public override void WriteRow(IList row, IReadOnlyList columns)
{
// Write the header for the object
xmlTextWriter.WriteStartElement(ItemElementTag);
// Write the items out as properties
- int columnStart = ColumnStartIndex ?? 0;
- int columnEnd = ColumnEndIndex + 1 ?? columns.Count;
- for (int i = columnStart; i < columnEnd; i++)
+ for (int i = ColumnStartIndex; i <= ColumnEndIndex; i++)
{
// Write the column name as item tag
xmlTextWriter.WriteStartElement(columns[i].ColumnName);
-
+
if (row[i].RawObject != null)
{
xmlTextWriter.WriteString(row[i].DisplayValue);
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs
index 51c917b9..6a3f96a6 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamFactory.cs
@@ -3,7 +3,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
+using System.Collections.Generic;
using System.IO;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Utility;
@@ -40,7 +42,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// A
public IFileStreamReader GetReader(string fileName)
{
- return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), QueryExecutionSettings);
+ return new ServiceBufferFileStreamReader(
+ new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
+ QueryExecutionSettings
+ );
}
///
@@ -48,10 +53,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// SSMS formatted buffer file, file share is ReadWrite to allow concurrent reads/writes to the file.
///
/// The file to write values to
+ ///
+ /// Ignored in order to fulfil the contract.
+ /// @TODO: Refactor this out so that save-as writers do not use the same contract as service buffer writers.
+ ///
/// A
- public IFileStreamWriter GetWriter(string fileName)
+ public IFileStreamWriter GetWriter(string fileName, IReadOnlyList columns)
{
- return new ServiceBufferFileStreamWriter(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite), QueryExecutionSettings);
+ return new ServiceBufferFileStreamWriter(
+ new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite),
+ QueryExecutionSettings
+ );
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs
index 3267b6c9..b57ba636 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamWriter.cs
@@ -167,7 +167,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
}
else
{
- // not a long field
+ // not a long field
values[i] = reader.GetValue(i);
}
}
@@ -209,7 +209,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
}
[Obsolete]
- public void WriteRow(IList row, IList columns)
+ public void WriteRow(IList row, IReadOnlyList columns)
{
throw new InvalidOperationException("This type of writer is meant to write values from a DbDataReader only.");
}
@@ -442,7 +442,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// Convert to a unicode byte array
byte[] bytes = Encoding.Unicode.GetBytes(sVal);
- // convert char array into byte array and write it out
+ // convert char array into byte array and write it out
iTotalLen = WriteLength(bytes.Length);
iTotalLen += FileUtilities.WriteWithLength(fileStream, bytes, bytes.Length);
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs
index cfdc2356..e56eff9e 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs
@@ -306,12 +306,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
///
- /// Generates the execution plan from the table returned
+ /// Generates the execution plan from the table returned
///
/// An execution plan object
public Task GetExecutionPlan()
{
- // Process the action just in case it hasn't been yet
+ // Process the action just in case it hasn't been yet
ProcessSpecialAction();
// Sanity check to make sure that results read has started
@@ -319,7 +319,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
throw new InvalidOperationException(SR.QueryServiceResultSetNotRead);
}
- // Check that we this result set contains a showplan
+ // Check that we this result set contains a showplan
if (!specialAction.ExpectYukonXMLShowPlan)
{
throw new Exception(SR.QueryServiceExecutionPlanNotFound);
@@ -327,7 +327,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
return Task.Factory.StartNew(() =>
- {
+ {
string content;
string format = null;
@@ -336,12 +336,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Determine the format and get the first col/row of XML
content = fileStreamReader.ReadRow(0, 0, Columns)[0].DisplayValue;
- if (specialAction.ExpectYukonXMLShowPlan)
+ if (specialAction.ExpectYukonXMLShowPlan)
{
format = "xml";
}
}
-
+
return new Contracts.ExecutionPlan
{
Format = format,
@@ -371,7 +371,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Open a writer for the file
//
- var fileWriter = fileStreamFactory.GetWriter(outputFileName);
+ var fileWriter = fileStreamFactory.GetWriter(outputFileName, null);
using (fileWriter)
{
// If we can initialize the columns using the column schema, use that
@@ -456,7 +456,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
///
- /// Updates the values in a row with the
+ /// Updates the values in a row with the
///
///
///
@@ -528,7 +528,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
using (var fileReader = fileFactory.GetReader(outputFileName))
- using (var fileWriter = fileFactory.GetWriter(saveParams.FilePath))
+ using (var fileWriter = fileFactory.GetWriter(saveParams.FilePath, Columns))
{
// Iterate over the rows that are in the selected row set
for (long i = rowStartIndex; i < rowEndIndex; ++i)
@@ -551,13 +551,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
}
});
-
+
// Add exception handling to the save task
Task taskWithHandling = saveAsTask.ContinueWithOnFaulted(async t =>
{
if (failureHandler != null)
{
- await failureHandler(saveParams, t.Exception.Message);
+ await failureHandler(saveParams, t.Exception?.Message);
}
});
@@ -691,7 +691,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
}
finally
- {
+ {
// Release the sendResultsSemphore so the next invocation gets unblocked
//
sendResultsSemphore.Release();
@@ -706,7 +706,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
///
/// If the result set represented by this class corresponds to a single XML
- /// column that contains results of "for xml" query, set isXml = true
+ /// column that contains results of "for xml" query, set isXml = true
/// If the result set represented by this class corresponds to a single JSON
/// column that contains results of "for json" query, set isJson = true
///
@@ -755,10 +755,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
///
/// Determine the special action, if any, for this result set
///
- private SpecialAction ProcessSpecialAction()
- {
+ private SpecialAction ProcessSpecialAction()
+ {
- // Check if this result set is a showplan
+ // Check if this result set is a showplan
if (Columns.Length == 1 && string.Compare(Columns[0].ColumnName, YukonXmlShowPlanColumn, StringComparison.OrdinalIgnoreCase) == 0)
{
specialAction.ExpectYukonXMLShowPlan = true;
@@ -780,7 +780,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
throw new InvalidOperationException(SR.QueryServiceResultSetNotRead);
}
- // NOTE: We are no longer checking to see if the data reader has rows before reading
+ // NOTE: We are no longer checking to see if the data reader has rows before reading
// b/c of a quirk in SqlClient. In some scenarios, a SqlException isn't thrown until we
// read. In order to get appropriate errors back to the user, we'll read first.
// Returning false from .ReadAsync means there aren't any rows.
@@ -791,7 +791,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
throw new InvalidOperationException(SR.QueryServiceResultSetAddNoRows);
}
-
+
using (IFileStreamWriter writer = fileStreamFactory.GetWriter(outputFileName))
{
// Write the row to the end of the file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/SerializationService.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/SerializationService.cs
index d46f78e3..c3b7a30a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/SerializationService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/SerializationService.cs
@@ -72,7 +72,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
inProgressSerializations.AddOrUpdate(serializer.FilePath, serializer, (key, old) => serializer);
}
-
+
Logger.Write(TraceEventType.Verbose, "HandleSerializeStartRequest");
SerializeDataResult result = serializer.ProcessRequest(serializeParams);
await requestContext.SendResult(result);
@@ -153,7 +153,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
private IFileStreamWriter writer;
private SerializeDataStartRequestParams requestParams;
- private IList columns;
+ private IReadOnlyList columns;
public string FilePath { get; private set; }
@@ -164,7 +164,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
this.FilePath = requestParams.FilePath;
}
- private IList MapColumns(ColumnInfo[] columns)
+ private IReadOnlyList MapColumns(ColumnInfo[] columns)
{
List columnWrappers = new List();
foreach (ColumnInfo column in columns)
@@ -258,7 +258,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
default:
throw new Exception(SR.SerializationServiceUnsupportedFormat(this.requestParams.SaveFormat));
}
- this.writer = factory.GetWriter(requestParams.FilePath);
+ this.writer = factory.GetWriter(requestParams.FilePath, columns);
}
}
public void CloseStreams()
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/MemoryFileSystem.cs b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/MemoryFileSystem.cs
index f3892127..0a8e7858 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/MemoryFileSystem.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/MemoryFileSystem.cs
@@ -5,7 +5,9 @@
using System;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.IO;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Moq;
@@ -32,8 +34,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
});
mock.Setup(fsf => fsf.GetReader(It.IsAny()))
.Returns(output => new ServiceBufferFileStreamReader(new MemoryStream(storage[output]), new QueryExecutionSettings()));
- mock.Setup(fsf => fsf.GetWriter(It.IsAny()))
- .Returns(output => new ServiceBufferFileStreamWriter(new MemoryStream(storage[output]), new QueryExecutionSettings()));
+ mock.Setup(fsf => fsf.GetWriter(It.IsAny(), It.IsAny>()))
+ .Returns>((output, _) => new ServiceBufferFileStreamWriter(new MemoryStream(storage[output]), new QueryExecutionSettings()));
return mock.Object;
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
index be56714e..5f1719a7 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
@@ -138,7 +138,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
s.EditCache[rs.RowCount] = mockEdit;
// If: I create a row in the session
- // Then:
+ // Then:
// ... An exception should be thrown
Assert.Throws(() => s.CreateRow());
@@ -321,7 +321,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
Mock emf = new Mock();
EditSession s = new EditSession(emf.Object);
- Assert.That(() => s.Initialize(initParams, c, qr, sh, fh), Throws.InstanceOf(), "I initialize it with a missing parameter. It should throw an exception");
+ Assert.Catch(() => s.Initialize(initParams, c, qr, sh, fh), "I initialize it with a missing parameter. It should throw an exception");
}
public static IEnumerable