mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-24 09:35:39 -05:00
* Copy smoModel some rename * Copy entire service layer * Building copy * Fixing some references * Launch profile * Resolve namespace issues * Compiling tests. Correct manifest. * Fixing localization resources * ReliableKustoClient * Some trimming of extra code and Kusto code * Kusto client creation in bindingContent * Removing Smo and new Kusto classes * More trimming * Kusto schema hookup * Solidying DataSource abstraction * Solidifying further * Latest refatoring * More refactoring * Building and launching Kusto service layer * Working model which enumerates databases * Refactoring to pass IDataSource to all tree nodes * Removing some dependencies on the context * Working with tables and schema * Comment checkin * Refactoring to give out select script * Query created and sent back to ADS * Fix query generation * Fix listing of databases * Tunneling the query through. * Successful query execution * Return only results table * Deleting Cms * Delete DacFx * Delete SchemaCompare and TaskServices * Change build definition to not stop at launch * Fix error after merge * Save Kusto results in different formats (#935) * save results as csv etc * some fixes Co-authored-by: Monica Gupta <mogupt@microsoft.com> * 2407 Added OrderBy clause in KustoDataSource > GetDatabaseMetaData and GetColumnMetadata (#959) * 2405 Defaulted Options when setting ServerInfo in ConnectionService > GetConnectionCompleteParams (#965) * 2747 Fixed IsUnknownType error for Kusto (#989) * 2747 Removed unused directives in Kusto > DbColumnWrapper. Refactored IsUnknownType to handle null DataTypeName * 2747 Reverted IsUnknownType change in DbColumnWrapper. Changed DataTypeName to get calue from ColumnType. Refactored SafeGetValue to type check before hard casting to reduce case exceptions. * Added EmbeddedResourceUseDependentUponConvention to Microsoft.Kusto.ServiceLayer.csproj. Also renamed DACfx to match Microsoft.SqlTools.ServiceLayer. Added to compile Exclude="**/obj/**/*.cs" * Srahman cleanup sql code (#992) * Removed Management and Security Service Code. * Remove FileBrowser service * Comment why we are using SqlServer library * Remove SQL specific type definitions * clean up formatter service (#996) Co-authored-by: Monica Gupta <mogupt@microsoft.com> * Code clean up and Kusto intellisense (#994) * Code clean up and Kusto intellisense * Addressed few comments * Addressed few comments * addressed comments Co-authored-by: Monica Gupta <mogupt@microsoft.com> * Return multiple tables for Kusto * Changes required for Kusto manage dashboard (#1039) * Changes required for manage dashboard * Addressed comments Co-authored-by: Monica Gupta <mogupt@microsoft.com> * 2728 Kusto function support (#1038) * loc update (#914) * loc update * loc updates * 2728 moved ColumnInfo and KustoResultsReader to separate files. Added Folder and Function to TreeNode.cs * 2728 Added FunctionInfo. Added Folder to ColumnInfo. Removed partial class from KustoResultsReader. Set Function.IsAlwaysLeaf=true in TreeNode.cs. In KustoDataSource changed tableMetadata type to TableMetaData. Added folder and function dictionaries. Refactored GetSchema function. Renamed GenerateColumnMetadataKey to GenerateMetadataKey * 2728 Added FunctionInfo. Added Folder to ColumnInfo. Removed partial class from KustoResultsReader. Set Function.IsAlwaysLeaf=true in TreeNode.cs. In KustoDataSource changed tableMetadata type to TableMetaData. Added folder and function dictionaries. Refactored GetSchema function. Renamed GenerateColumnMetadataKey to GenerateMetadataKey * 2728 Created new SqlConnection within using block. Refactored KustoDataSource > columnmetadata to sort on get instead of insert. * 2728 Added GetFunctionInfo function to KustoDataSource. * 2728 Reverted change to Microsoft.Kusto.ServiceLayer.csproj from merge * 2728 Reverted change to SqlTools.ServiceLayer\Localization\transXliff * 2728 Reverted change to sr.de.xlf and sr.zh-hans.xlf * 2728 Refactored KustoDataSource Function folders to support subfolders * 2728 Refactored KustoDataSource to use urn for folders, functions, and tables instead of name. * Merge remote-tracking branch 'origin/main' into feature-ADE # Conflicts: # Packages.props * 2728 Moved metadata files into Metadata subdirectory. Added GenerateAlterFunction to IDataSource and DataSourceBase. * 2728 Added summary information to SafeAdd in SystemExtensions. Renamed local variable in SetTableMetadata * 2728 Moved SafeAdd from SystemExtensions to KustoQueryUtils. Added check when getting database schema to return existing records before querying again. Added AddRange function to KustoQueryUtils. Created SetFolderMetadataForFunctions method. * 2728 Added DatabaseKeyPrefix to only return tables to a database for the dashboard. Added logic to store all database tables within the tableMetadata dictionary for the dashboard. * 2728 Created TableInfo and moved info objects into Models directory. Refactored KustoDataSource to lazy load columns for tables. Refactored logic to load tables using cslschema instead of schema. * 2728 Renamed LoadColumnSchema to GetTableSchema to be consistent. Co-authored-by: khoiph1 <khoiph@microsoft.com> * Addressed comments Co-authored-by: Shafiq Rahman <srahman@microsoft.com> Co-authored-by: Monica Gupta <mogupt@microsoft.com> Co-authored-by: Justin M <63619224+JustinMDotNet@users.noreply.github.com> Co-authored-by: rkselfhost <rkselfhost@outlook.com> Co-authored-by: khoiph1 <khoiph@microsoft.com>
161 lines
6.5 KiB
C#
161 lines
6.5 KiB
C#
//
|
|
// 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 System.Linq;
|
|
using System.Text;
|
|
using Microsoft.Kusto.ServiceLayer.QueryExecution.Contracts;
|
|
|
|
namespace Microsoft.Kusto.ServiceLayer.QueryExecution.DataStorage
|
|
{
|
|
/// <summary>
|
|
/// Writer for writing rows of results to a CSV file
|
|
/// </summary>
|
|
public class SaveAsCsvFileStreamWriter : SaveAsStreamWriter
|
|
{
|
|
|
|
#region Member Variables
|
|
|
|
private readonly SaveResultsAsCsvRequestParams saveParams;
|
|
private bool headerWritten;
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Constructor, stores the CSV specific request params locally, chains into the base
|
|
/// constructor
|
|
/// </summary>
|
|
/// <param name="stream">FileStream to access the CSV file output</param>
|
|
/// <param name="requestParams">CSV save as request parameters</param>
|
|
public SaveAsCsvFileStreamWriter(Stream stream, SaveResultsAsCsvRequestParams requestParams)
|
|
: base(stream, requestParams)
|
|
{
|
|
saveParams = requestParams;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a row of data as a CSV row. If this is the first row and the user has requested
|
|
/// it, the headers for the column will be emitted as well.
|
|
/// </summary>
|
|
/// <param name="row">The data of the row to output to the file</param>
|
|
/// <param name="columns">
|
|
/// The entire list of columns for the result set. They will be filtered down as per the
|
|
/// request params.
|
|
/// </param>
|
|
public override void WriteRow(IList<DbCellValue> row, IList<DbColumnWrapper> 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));
|
|
string rowLine = string.Join(delimiter, selectedCells);
|
|
|
|
// Encode it and write it out
|
|
byte[] rowBytes = encoding.GetBytes(rowLine + lineSeperator);
|
|
FileStream.Write(rowBytes, 0, rowBytes.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encodes a single field for inserting into a CSV record. The following rules are applied:
|
|
/// <list type="bullet">
|
|
/// <item><description>All double quotes (") are replaced with a pair of consecutive double quotes</description></item>
|
|
/// </list>
|
|
/// The entire field is also surrounded by a pair of double quotes if any of the following conditions are met:
|
|
/// <list type="bullet">
|
|
/// <item><description>The field begins or ends with a space</description></item>
|
|
/// <item><description>The field begins or ends with a tab</description></item>
|
|
/// <item><description>The field contains the ListSeparator string</description></item>
|
|
/// <item><description>The field contains the '\n' character</description></item>
|
|
/// <item><description>The field contains the '\r' character</description></item>
|
|
/// <item><description>The field contains the '"' character</description></item>
|
|
/// </list>
|
|
/// </summary>
|
|
/// <param name="field">The field to encode</param>
|
|
/// <returns>The CSV encoded version of the original field</returns>
|
|
internal static string EncodeCsvField(string field, char delimiter, char textIdentifier)
|
|
{
|
|
string strTextIdentifier = textIdentifier.ToString();
|
|
|
|
// Special case for nulls
|
|
if (field == null)
|
|
{
|
|
return "NULL";
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
} |