mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-25 17:24:17 -05:00
Added new Kusto ServiceLayer (#1009)
* 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>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters to cancel a scripting request.
|
||||
/// </summary>
|
||||
public class ScriptingCancelParams : ScriptingEventParams { }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters returned from a scripting request.
|
||||
/// </summary>
|
||||
public class ScriptingCancelResult { }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the scripting cancel request type.
|
||||
/// </summary>
|
||||
public class ScriptingCancelRequest
|
||||
{
|
||||
public static readonly RequestType<ScriptingCancelParams, ScriptingCancelResult> Type =
|
||||
RequestType<ScriptingCancelParams, ScriptingCancelResult>.Create("scripting/scriptCancel");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// 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.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters sent to when a scripting operation has completed.
|
||||
/// </summary>
|
||||
public class ScriptingCompleteParams : ScriptingEventParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Get or sets the error details for an error that occurred during the scripting operation.
|
||||
/// </summary>
|
||||
public string ErrorDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets the error message for an error that occurred during the scripting operation.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets a value to indicate an error occurred during the scripting operation.
|
||||
/// </summary>
|
||||
public bool HasError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets a value to indicate the scripting operation was canceled.
|
||||
/// </summary>
|
||||
public bool Canceled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets a value to indicate the scripting operation successfully completed.
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event sent to indicate a scripting operation has completed.
|
||||
/// </summary>
|
||||
public class ScriptingCompleteEvent
|
||||
{
|
||||
public static readonly EventType<ScriptingCompleteParams> Type =
|
||||
EventType<ScriptingCompleteParams>.Create("scripting/scriptComplete");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all scripting event parameters.
|
||||
/// </summary>
|
||||
public abstract class ScriptingEventParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the operation id of the scripting operation this event is associated with.
|
||||
/// </summary>
|
||||
public string OperationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sequence number. The sequence number starts at 1, and is incremented each time a scripting event is
|
||||
/// raised for the current scripting operation.
|
||||
/// </summary>
|
||||
public int SequenceNumber { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters sent when a list objects operation has completed.
|
||||
/// </summary>
|
||||
public class ScriptingListObjectsCompleteParams : ScriptingCompleteParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of database objects returned from the list objects operation.
|
||||
/// </summary>
|
||||
public List<ScriptingObject> ScriptingObjects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the count of database object returned from the list objects operation.
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event sent to indicate a list objects operation has completed.
|
||||
/// </summary>
|
||||
public class ScriptingListObjectsCompleteEvent
|
||||
{
|
||||
public static readonly EventType<ScriptingListObjectsCompleteParams> Type =
|
||||
EventType<ScriptingListObjectsCompleteParams>.Create("scripting/listObjectsComplete");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for a list objects request.
|
||||
/// </summary>
|
||||
public class ScriptingListObjectsParams
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters returned from a list objects request.
|
||||
/// </summary>
|
||||
public class ScriptingListObjectsResult
|
||||
{
|
||||
public string OperationId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the scripting list objects request type.
|
||||
/// </summary>
|
||||
public class ScriptingListObjectsRequest
|
||||
{
|
||||
public static readonly RequestType<ScriptingListObjectsParams, ScriptingListObjectsResult> Type =
|
||||
RequestType<ScriptingListObjectsParams, ScriptingListObjectsResult>.Create("scripting/listObjects");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to represent a database object that can be scripted.
|
||||
/// </summary>
|
||||
public sealed class ScriptingObject : IEquatable<ScriptingObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the database object type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This underlying values are determined by the SqlScriptPublishModel.GetDatabaseObjectTypes() and
|
||||
/// can change depending on the version of SMO used by the tools service. Values can be:
|
||||
/// Table,
|
||||
/// View,
|
||||
/// StoredProcedure,
|
||||
/// UserDefinedFunction,
|
||||
/// UserDefinedDataType,
|
||||
/// User,
|
||||
/// Default,
|
||||
/// Rule,
|
||||
/// DatabaseRole,
|
||||
/// ApplicationRole,
|
||||
/// SqlAssembly,
|
||||
/// DdlTrigger,
|
||||
/// Synonym,
|
||||
/// XmlSchemaCollection,
|
||||
/// Schema,
|
||||
/// PlanGuide,
|
||||
/// UserDefinedType,
|
||||
/// UserDefinedAggregate,
|
||||
/// FullTextCatalog,
|
||||
/// UserDefinedTableType
|
||||
/// </remarks>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the schema of the database object.
|
||||
/// </summary>
|
||||
public string Schema { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the database object name.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string objectName = string.IsNullOrEmpty(this.Schema)
|
||||
? this.Name
|
||||
: this.Schema + "." + this.Name;
|
||||
|
||||
return objectName;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Type ?? string.Empty) ^
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Schema ?? string.Empty) ^
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Name ?? string.Empty);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return
|
||||
obj != null &&
|
||||
this.GetType() == obj.GetType() &&
|
||||
this.Equals((ScriptingObject)obj);
|
||||
}
|
||||
|
||||
public bool Equals(ScriptingObject other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
string.Equals(this.Type, other.Type, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.Schema, other.Schema, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Scripting Operation type
|
||||
/// </summary>
|
||||
public enum ScriptingOperationType
|
||||
{
|
||||
Select = 0,
|
||||
Create = 1,
|
||||
Insert = 2,
|
||||
Update = 3,
|
||||
Delete = 4,
|
||||
Execute = 5,
|
||||
Alter = 6
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the scripting options.
|
||||
/// </summary>
|
||||
public class ScriptOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate ANSI padding statements
|
||||
/// </summary>
|
||||
public virtual bool? ScriptAnsiPadding { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Append the generated script to a file
|
||||
/// </summary>
|
||||
public virtual bool? AppendToFile { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Continue to script if an error occurs. Otherwise, stop.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public virtual bool? ContinueScriptingOnError { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Convert user-defined data types to base types.
|
||||
/// </summary>
|
||||
public virtual bool? ConvertUDDTToBaseType { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Generate script for dependent objects for each object scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is false.
|
||||
/// </remarks>
|
||||
public virtual bool? GenerateScriptForDependentObjects { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Include descriptive headers for each object generated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public virtual bool? IncludeDescriptiveHeaders { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Check that an object with the given name exists before dropping or altering or that an object with the given name does not exist before creating.
|
||||
/// </summary>
|
||||
public virtual bool? IncludeIfNotExists { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script options to set vardecimal storage format.
|
||||
/// </summary>
|
||||
public virtual bool? IncludeVarDecimal { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Include system generated constraint names to enforce declarative referential integrity.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptDriIncludeSystemNames { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Include statements in the script that are not supported on the specified SQL Server database engine type.
|
||||
/// </summary>
|
||||
public virtual bool? IncludeUnsupportedStatements { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Prefix object names with the object schema.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public virtual bool? SchemaQualify { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script options to set bindings option.
|
||||
/// </summary>
|
||||
public virtual bool? Bindings { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script the objects that use collation.
|
||||
/// </summary>
|
||||
public virtual bool? Collation { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script the default values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public virtual bool? Default { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script Object CREATE/DROP statements.
|
||||
/// Possible values:
|
||||
/// ScriptCreate
|
||||
/// ScriptDrop
|
||||
/// ScriptCreateDrop
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is ScriptCreate.
|
||||
/// </remarks>
|
||||
public virtual string ScriptCreateDrop { get; set; } = "ScriptCreate";
|
||||
|
||||
/// <summary>
|
||||
/// Script the Extended Properties for each object scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public virtual bool? ScriptExtendedProperties { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script only features compatible with the specified version of SQL Server. Possible values:
|
||||
/// Script90Compat
|
||||
/// Script100Compat
|
||||
/// Script105Compat
|
||||
/// Script110Compat
|
||||
/// Script120Compat
|
||||
/// Script130Compat
|
||||
/// Script140Compat
|
||||
/// Script150Compat
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is Script140Compat.
|
||||
/// </remarks>
|
||||
public virtual string ScriptCompatibilityOption { get; set; } = "Script140Compat";
|
||||
|
||||
/// <summary>
|
||||
/// Script only features compatible with the specified SQL Server database engine type.
|
||||
/// Possible Values:
|
||||
/// SingleInstance
|
||||
/// SqlAzure
|
||||
/// </summary>
|
||||
public virtual string TargetDatabaseEngineType { get; set; } = "SingleInstance";
|
||||
|
||||
/// <summary>
|
||||
/// Script only features compatible with the specified SQL Server database engine edition.
|
||||
/// Possible Values:
|
||||
/// SqlServerPersonalEdition
|
||||
/// SqlServerStandardEdition
|
||||
/// SqlServerEnterpriseEdition
|
||||
/// SqlServerExpressEdition
|
||||
/// SqlAzureDatabaseEdition
|
||||
/// SqlDatawarehouseEdition
|
||||
/// SqlServerStretchEdition
|
||||
/// SqlManagedInstanceEdition
|
||||
/// </summary>
|
||||
public virtual string TargetDatabaseEngineEdition { get; set; } = "SqlServerEnterpriseEdition";
|
||||
|
||||
/// <summary>
|
||||
/// Script all logins available on the server. Passwords will not be scripted.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptLogins { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Generate object-level permissions.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptObjectLevelPermissions { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script owner for the objects.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptOwner { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script statistics, and optionally include histograms, for each selected table or view.
|
||||
/// Possible values:
|
||||
/// ScriptStatsNone
|
||||
/// ScriptStatsDDL
|
||||
/// ScriptStatsAll
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is ScriptStatsNone.
|
||||
/// </remarks>
|
||||
public virtual string ScriptStatistics { get; set; } = "ScriptStatsNone";
|
||||
|
||||
/// <summary>
|
||||
/// Generate USE DATABASE statement.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptUseDatabase { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Generate script that contains schema only or schema and data.
|
||||
/// Possible Values:
|
||||
/// SchemaAndData
|
||||
/// DataOnly
|
||||
/// SchemaOnly
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is SchemaOnly.
|
||||
/// </remarks>
|
||||
public virtual string TypeOfDataToScript { get; set; } = "SchemaOnly";
|
||||
|
||||
/// <summary>
|
||||
/// Scripts the change tracking information.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptChangeTracking { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script the check constraints for each table or view scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public virtual bool? ScriptCheckConstraints { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Scripts the data compression information.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptDataCompressionOptions { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Script the foreign keys for each table scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public virtual bool? ScriptForeignKeys { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script the full-text indexes for each table or indexed view scripted.
|
||||
/// </summary>
|
||||
public virtual bool? ScriptFullTextIndexes { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script the indexes (including XML and clustered indexes) for each table or indexed view scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public virtual bool? ScriptIndexes { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script the primary keys for each table or view scripted
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public virtual bool? ScriptPrimaryKeys { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script the triggers for each table or view scripted
|
||||
/// </summary>
|
||||
public virtual bool? ScriptTriggers { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Script the unique keys for each table or view scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public virtual bool? UniqueKeys { get; set; } = true;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters to indicate the script operation has resolved the objects to be scripted.
|
||||
/// </summary>
|
||||
public class ScriptingPlanNotificationParams : ScriptingEventParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of database objects whose progress has changed.
|
||||
/// </summary>
|
||||
public List<ScriptingObject> ScriptingObjects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the count of database objects whose progress has changed.
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event sent to indicate a script operation has determined which objects will be scripted.
|
||||
/// </summary>
|
||||
public class ScriptingPlanNotificationEvent
|
||||
{
|
||||
public static readonly EventType<ScriptingPlanNotificationParams> Type = EventType<ScriptingPlanNotificationParams>.Create("scripting/scriptPlanNotification");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters sent when a scripting operation has made progress.
|
||||
/// </summary>
|
||||
public class ScriptingProgressNotificationParams : ScriptingEventParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the scripting object whose progress has changed.
|
||||
/// </summary>
|
||||
public ScriptingObject ScriptingObject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status of the scripting operation for the scripting object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Values can be: 'Completed', 'Progress', and 'Error'.
|
||||
/// </remarks>
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or count of completed scripting operations.
|
||||
/// </summary>
|
||||
public int CompletedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets this total count of objects to script.
|
||||
/// </summary>
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the error details if an error occurred scripting a database object.
|
||||
/// </summary>
|
||||
public string ErrorDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets the error message for an error that occurred scripting a database object.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event to indicate the scripting operation has made progress.
|
||||
/// </summary>
|
||||
public class ScriptingProgressNotificationEvent
|
||||
{
|
||||
public static readonly EventType<ScriptingProgressNotificationParams> Type =
|
||||
EventType<ScriptingProgressNotificationParams>.Create("scripting/scriptProgressNotification");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for a script request.
|
||||
/// </summary>
|
||||
public class ScriptingParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the file path used when writing out the script.
|
||||
/// </summary>
|
||||
public string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether scripting to a single file or file per object.
|
||||
/// </summary>
|
||||
public string ScriptDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets connection string of the target database the scripting operation will run against.
|
||||
/// </summary>
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of scripting objects to script.
|
||||
/// </summary>
|
||||
public List<ScriptingObject> ScriptingObjects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of scripting object which specify the include criteria of objects to script.
|
||||
/// </summary>
|
||||
public List<ScriptingObject> IncludeObjectCriteria { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of scripting object which specify the exclude criteria of objects to not script.
|
||||
/// </summary>
|
||||
public List<ScriptingObject> ExcludeObjectCriteria { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of schema name of objects to script.
|
||||
/// </summary>
|
||||
public List<string> IncludeSchemas { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of schema name of objects to not script.
|
||||
/// </summary>
|
||||
public List<string> ExcludeSchemas { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of type name of objects to script.
|
||||
/// </summary>
|
||||
public List<string> IncludeTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of type name of objects to not script
|
||||
/// </summary>
|
||||
public List<string> ExcludeTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scripting options.
|
||||
/// </summary>
|
||||
public ScriptOptions ScriptOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the connection owner URI
|
||||
/// </summary>
|
||||
public string OwnerUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The script operation
|
||||
/// </summary>
|
||||
public ScriptingOperationType Operation { get; set; } = ScriptingOperationType.Create;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters returned from a script request.
|
||||
/// </summary>
|
||||
public class ScriptingResult
|
||||
{
|
||||
public string OperationId { get; set; }
|
||||
|
||||
public string Script { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the scripting request type.
|
||||
/// </summary>
|
||||
public class ScriptingRequest
|
||||
{
|
||||
public static readonly RequestType<ScriptingParams, ScriptingResult> Type =
|
||||
RequestType<ScriptingParams, ScriptingResult>.Create("scripting/script");
|
||||
}
|
||||
}
|
||||
712
src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptAsOptions.cs
Normal file
712
src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptAsOptions.cs
Normal file
@@ -0,0 +1,712 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrpaper of ScriptOptions to map the option name with the oen in SMO.ScriptingOptions
|
||||
/// </summary>
|
||||
public class ScriptAsOptions : ScriptOptions
|
||||
{
|
||||
public ScriptAsOptions(ScriptOptions scriptOptions)
|
||||
{
|
||||
Validate.IsNotNull(nameof(scriptOptions), scriptOptions);
|
||||
ScriptOptions = scriptOptions;
|
||||
}
|
||||
|
||||
public ScriptOptions ScriptOptions { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Generate ANSI padding statements
|
||||
/// </summary>
|
||||
public override bool? ScriptAnsiPadding
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptAnsiPadding;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptAnsiPadding = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Append the generated script to a file
|
||||
/// </summary>
|
||||
public override bool? AppendToFile
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.AppendToFile;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.AppendToFile = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continue to script if an error occurs. Otherwise, stop.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public override bool? ContinueScriptingOnError
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ContinueScriptingOnError;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ContinueScriptingOnError = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert user-defined data types to base types.
|
||||
/// </summary>
|
||||
public override bool? ConvertUDDTToBaseType
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ConvertUDDTToBaseType;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ConvertUDDTToBaseType = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate script for dependent objects for each object scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is false.
|
||||
/// </remarks>
|
||||
public override bool? GenerateScriptForDependentObjects
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.GenerateScriptForDependentObjects;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.GenerateScriptForDependentObjects = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include descriptive headers for each object generated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public override bool? IncludeDescriptiveHeaders
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.IncludeDescriptiveHeaders;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.IncludeDescriptiveHeaders = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that an object with the given name exists before dropping or altering or that an object with the given name does not exist before creating.
|
||||
/// </summary>
|
||||
public override bool? IncludeIfNotExists
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.IncludeIfNotExists;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.IncludeIfNotExists = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script options to set vardecimal storage format.
|
||||
/// </summary>
|
||||
public override bool? IncludeVarDecimal
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.IncludeVarDecimal;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.IncludeVarDecimal = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include system generated constraint names to enforce declarative referential integrity.
|
||||
/// </summary>
|
||||
public override bool? ScriptDriIncludeSystemNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptDriIncludeSystemNames;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptDriIncludeSystemNames = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include statements in the script that are not supported on the specified SQL Server database engine type.
|
||||
/// </summary>
|
||||
public override bool? IncludeUnsupportedStatements
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.IncludeUnsupportedStatements;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.IncludeUnsupportedStatements = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix object names with the object schema.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public override bool? SchemaQualify
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.SchemaQualify;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.SchemaQualify = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script options to set bindings option.
|
||||
/// </summary>
|
||||
public override bool? Bindings
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.Bindings;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.Bindings = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the objects that use collation.
|
||||
/// </summary>
|
||||
public override bool? Collation
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.Collation;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.Collation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the default values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public override bool? Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.Default;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.Default = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script Object CREATE/DROP statements.
|
||||
/// Possible values:
|
||||
/// ScriptCreate
|
||||
/// ScriptDrop
|
||||
/// ScriptCreateDrop
|
||||
/// ScriptSelect
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is ScriptCreate.
|
||||
/// </remarks>
|
||||
public override string ScriptCreateDrop
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptCreateDrop;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptCreateDrop = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the Extended Properties for each object scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is true.
|
||||
/// </remarks>
|
||||
public override bool? ScriptExtendedProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptExtendedProperties;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptExtendedProperties = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script only features compatible with the specified version of SQL Server. Possible values:
|
||||
/// Script90Compat
|
||||
/// Script100Compat
|
||||
/// Script105Compat
|
||||
/// Script110Compat
|
||||
/// Script120Compat
|
||||
/// Script130Compat
|
||||
/// Script140Compat
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default is Script140Compat.
|
||||
/// </remarks>
|
||||
public override string ScriptCompatibilityOption
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptCompatibilityOption;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptCompatibilityOption = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script only features compatible with the specified SQL Server database engine type.
|
||||
/// Possible Values:
|
||||
/// SingleInstance
|
||||
/// SqlAzure
|
||||
/// </summary>
|
||||
public override string TargetDatabaseEngineType
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.TargetDatabaseEngineType;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.TargetDatabaseEngineType = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script only features compatible with the specified SQL Server database engine edition.
|
||||
/// Possible Values:
|
||||
/// SqlServerPersonalEdition
|
||||
/// SqlServerStandardEdition
|
||||
/// SqlServerEnterpriseEdition
|
||||
/// SqlServerExpressEdition
|
||||
/// SqlAzureDatabaseEdition
|
||||
/// SqlDatawarehouseEdition
|
||||
/// SqlServerStretchEdition
|
||||
/// </summary>
|
||||
public override string TargetDatabaseEngineEdition
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.TargetDatabaseEngineEdition;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.TargetDatabaseEngineEdition = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script all logins available on the server. Passwords will not be scripted.
|
||||
/// </summary>
|
||||
public override bool? ScriptLogins
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptLogins;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptLogins = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate object-level permissions.
|
||||
/// </summary>
|
||||
public override bool? ScriptObjectLevelPermissions
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptObjectLevelPermissions;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptObjectLevelPermissions = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script owner for the objects.
|
||||
/// </summary>
|
||||
public override bool? ScriptOwner
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptOwner;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptOwner = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script statistics, and optionally include histograms, for each selected table or view.
|
||||
/// Possible values:
|
||||
/// ScriptStatsNone
|
||||
/// ScriptStatsDDL
|
||||
/// ScriptStatsAll
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is ScriptStatsNone.
|
||||
/// </remarks>
|
||||
public override string ScriptStatistics
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptStatistics;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptStatistics = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate USE DATABASE statement.
|
||||
/// </summary>
|
||||
public override bool? ScriptUseDatabase
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptUseDatabase;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptUseDatabase = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate script that contains schema only or schema and data.
|
||||
/// Possible Values:
|
||||
/// SchemaAndData
|
||||
/// DataOnly
|
||||
/// SchemaOnly
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is SchemaOnly.
|
||||
/// </remarks>
|
||||
public override string TypeOfDataToScript
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.TypeOfDataToScript;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.TypeOfDataToScript = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scripts the change tracking information.
|
||||
/// </summary>
|
||||
public override bool? ScriptChangeTracking
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptChangeTracking;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptChangeTracking = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the check constraints for each table or view scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public override bool? ScriptCheckConstraints
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptCheckConstraints;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptCheckConstraints = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scripts the data compression information.
|
||||
/// </summary>
|
||||
public override bool? ScriptDataCompressionOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptDataCompressionOptions;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptDataCompressionOptions = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the foreign keys for each table scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public override bool? ScriptForeignKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptForeignKeys;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptForeignKeys = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the full-text indexes for each table or indexed view scripted.
|
||||
/// </summary>
|
||||
public override bool? ScriptFullTextIndexes
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptFullTextIndexes;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptFullTextIndexes = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the indexes (including XML and clustered indexes) for each table or indexed view scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public override bool? ScriptIndexes
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptIndexes;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptIndexes = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the primary keys for each table or view scripted
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public override bool? ScriptPrimaryKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptPrimaryKeys;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptPrimaryKeys = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the triggers for each table or view scripted
|
||||
/// </summary>
|
||||
public override bool? ScriptTriggers
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.ScriptTriggers;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.ScriptTriggers = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script the unique keys for each table or view scripted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is true.
|
||||
/// </remarks>
|
||||
public override bool? UniqueKeys
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScriptOptions.UniqueKeys;
|
||||
}
|
||||
set
|
||||
{
|
||||
ScriptOptions.UniqueKeys = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Generate ANSI padding statements
|
||||
/// </summary>
|
||||
public bool? AnsiPadding { get { return ScriptOptions.ScriptAnsiPadding; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns ConvertUDDTToBaseType
|
||||
/// </summary>
|
||||
public bool? ConvertUserDefinedDataTypesToBaseType { get { return ScriptOptions.ConvertUDDTToBaseType; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns IncludeDescriptiveHeaders
|
||||
/// </summary>
|
||||
public bool? IncludeHeaders { get { return ScriptOptions.IncludeDescriptiveHeaders; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns ScriptDriIncludeSystemNames
|
||||
/// </summary>
|
||||
public bool? DriIncludeSystemNames { get { return ScriptOptions.ScriptDriIncludeSystemNames; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns SchemaQualify
|
||||
/// </summary>
|
||||
public bool? SchemaQualifyForeignKeysReferences { get { return ScriptOptions.SchemaQualify; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if Collation is true
|
||||
/// </summary>
|
||||
public bool? NoCollation { get { return !ScriptOptions.Collation; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of Default Property
|
||||
/// </summary>
|
||||
public bool? DriDefaults { get { return ScriptOptions.Default; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptExtendedProperties Property
|
||||
/// </summary>
|
||||
public bool? ExtendedProperties { get { return ScriptOptions.ScriptExtendedProperties; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptObjectLevelPermissions Property
|
||||
/// </summary>
|
||||
public bool? Permissions { get { return ScriptOptions.ScriptObjectLevelPermissions; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptStatistics Property
|
||||
/// </summary>
|
||||
public string Statistics { get { return ScriptOptions.ScriptStatistics; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptChangeTracking Property
|
||||
/// </summary>
|
||||
public bool? ChangeTracking { get { return ScriptOptions.ScriptChangeTracking; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptCheckConstraints Property
|
||||
/// </summary>
|
||||
public bool? DriChecks { get { return ScriptOptions.ScriptCheckConstraints; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptDataCompressionOptions Property
|
||||
/// </summary>
|
||||
public bool? ScriptDataCompression { get { return ScriptOptions.ScriptDataCompressionOptions; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptForeignKeys Property
|
||||
/// </summary>
|
||||
public bool? DriForeignKeys { get { return ScriptOptions.ScriptForeignKeys; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptFullTextIndexes Property
|
||||
/// </summary>
|
||||
public bool? FullTextIndexes { get { return ScriptOptions.ScriptFullTextIndexes; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptIndexes Property
|
||||
/// </summary>
|
||||
public bool? Indexes { get { return ScriptOptions.ScriptIndexes; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptPrimaryKeys Property
|
||||
/// </summary>
|
||||
public bool? DriPrimaryKey { get { return ScriptOptions.ScriptPrimaryKeys; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of ScriptTriggers Property
|
||||
/// </summary>
|
||||
public bool? Triggers { get { return ScriptOptions.ScriptTriggers; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of UniqueKeys Property
|
||||
/// </summary>
|
||||
public bool? DriUniqueKeys { get { return ScriptOptions.UniqueKeys; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
//
|
||||
// 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.Data.SqlClient;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using Microsoft.SqlServer.Management.SqlScriptPublish;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to generate script as for one smo object
|
||||
/// </summary>
|
||||
public class ScriptAsScriptingOperation : SmoScriptingOperation
|
||||
{
|
||||
private static readonly Dictionary<string, SqlServerVersion> scriptCompatibilityMap = LoadScriptCompatibilityMap();
|
||||
/// <summary>
|
||||
/// Left delimiter for an named object
|
||||
/// </summary>
|
||||
public const char LeftDelimiter = '[';
|
||||
|
||||
/// <summary>
|
||||
/// right delimiter for a named object
|
||||
/// </summary>
|
||||
public const char RightDelimiter = ']';
|
||||
|
||||
public ScriptAsScriptingOperation(ScriptingParams parameters, IDataSource dataSource): base(parameters)
|
||||
{
|
||||
Validate.IsNotNull("dataSource", dataSource);
|
||||
DataSource = dataSource;
|
||||
}
|
||||
|
||||
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken) : base(parameters)
|
||||
{
|
||||
DataSource = DataSourceFactory.Create(DataSourceType.Kusto, this.Parameters.ConnectionString, azureAccountToken);
|
||||
}
|
||||
|
||||
internal IDataSource DataSource { get; set; }
|
||||
|
||||
private string serverName;
|
||||
private string databaseName;
|
||||
private bool disconnectAtDispose = false;
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
this.ValidateScriptDatabaseParams();
|
||||
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
string resultScript = string.Empty;
|
||||
|
||||
UrnCollection urns = CreateUrns(DataSource);
|
||||
ScriptingOptions options = new ScriptingOptions();
|
||||
SetScriptBehavior(options);
|
||||
ScriptAsOptions scriptAsOptions = new ScriptAsOptions(this.Parameters.ScriptOptions);
|
||||
PopulateAdvancedScriptOptions(scriptAsOptions, options);
|
||||
options.WithDependencies = false;
|
||||
// TODO: Not including the header by default. We have to get this option from client
|
||||
options.IncludeHeaders = false;
|
||||
|
||||
// Scripting data is not avaialable in the scripter
|
||||
options.ScriptData = false;
|
||||
SetScriptingOptions(options);
|
||||
|
||||
switch (this.Parameters.Operation)
|
||||
{
|
||||
case ScriptingOperationType.Select:
|
||||
resultScript = GenerateScriptSelect(DataSource, urns);
|
||||
break;
|
||||
|
||||
case ScriptingOperationType.Alter:
|
||||
resultScript = GenerateScriptAlter(DataSource, urns);
|
||||
break;
|
||||
}
|
||||
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending script complete notification event for operation {0}",
|
||||
this.OperationId
|
||||
));
|
||||
|
||||
ScriptText = resultScript;
|
||||
|
||||
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
|
||||
{
|
||||
Success = true,
|
||||
});
|
||||
|
||||
this.SendPlanNotificationEvent(new ScriptingPlanNotificationParams
|
||||
{
|
||||
ScriptingObjects = this.Parameters.ScriptingObjects,
|
||||
Count = 1,
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e.IsOperationCanceledException())
|
||||
{
|
||||
Logger.Write(TraceEventType.Information, string.Format("Scripting operation {0} was canceled", this.OperationId));
|
||||
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
|
||||
{
|
||||
Canceled = true,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Write(TraceEventType.Error, string.Format("Scripting operation {0} failed with exception {1}", this.OperationId, e));
|
||||
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
|
||||
{
|
||||
OperationId = OperationId,
|
||||
HasError = true,
|
||||
ErrorMessage = $"{SR.ScriptingGeneralError} {e.Message}",
|
||||
ErrorDetails = e.ToString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disconnectAtDispose && DataSource != null)
|
||||
{
|
||||
DataSource.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateScriptSelect(IDataSource dataSource, UrnCollection urns)
|
||||
{
|
||||
ScriptingObject scriptingObject = this.Parameters.ScriptingObjects[0];
|
||||
Urn objectUrn = urns[0];
|
||||
|
||||
// select from table
|
||||
if (string.Equals(scriptingObject.Type, "Table", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return new Scripter().SelectFromTableOrView(dataSource, objectUrn);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string GenerateScriptAlter(IDataSource dataSource, UrnCollection urns)
|
||||
{
|
||||
ScriptingObject scriptingObject = this.Parameters.ScriptingObjects[0];
|
||||
Urn objectUrn = urns[0];
|
||||
|
||||
if (string.Equals(scriptingObject.Type, "Function", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return new Scripter().AlterFunction(dataSource, scriptingObject);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generate a schema qualified name (e.g. [schema].[objectName]) for an object if the option for SchemaQualify is true
|
||||
/// </summary>
|
||||
/// <param name="schema">The schema name. May be null or empty in which case it will be ignored</param>
|
||||
/// <param name="objectName">The object name.</param>
|
||||
/// <param name="schemaQualify">Whether to schema qualify the object or not</param>
|
||||
/// <returns>The object name, quoted as appropriate and schema-qualified if the option is set</returns>
|
||||
static private string GenerateSchemaQualifiedName(string schema, string objectName, bool schemaQualify)
|
||||
{
|
||||
var qualifiedName = new StringBuilder();
|
||||
|
||||
if (schemaQualify && !String.IsNullOrEmpty(schema))
|
||||
{
|
||||
// schema.name
|
||||
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}.{1}", GetDelimitedString(schema), GetDelimitedString(objectName));
|
||||
}
|
||||
else
|
||||
{
|
||||
// name
|
||||
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}", GetDelimitedString(objectName));
|
||||
}
|
||||
|
||||
return qualifiedName.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// getting delimited string
|
||||
/// </summary>
|
||||
/// <param name="str">string</param>
|
||||
/// <returns>string</returns>
|
||||
static private string GetDelimitedString(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder qualifiedName = new StringBuilder();
|
||||
qualifiedName.AppendFormat("{0}{1}{2}",
|
||||
LeftDelimiter,
|
||||
QuoteObjectName(str),
|
||||
RightDelimiter);
|
||||
return qualifiedName.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// turn a smo datatype object into a type that can be inserted into tsql, e.g. nvarchar(20)
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
internal static string GetDatatype(DataType type, ScriptingOptions options)
|
||||
{
|
||||
// string we'll return.
|
||||
string rv = string.Empty;
|
||||
|
||||
string dataType = type.Name;
|
||||
switch (type.SqlDataType)
|
||||
{
|
||||
// char, nchar, nchar, nvarchar, varbinary, nvarbinary are all displayed as type(length)
|
||||
// length of -1 is taken to be type(max). max isn't localizable.
|
||||
case SqlDataType.Char:
|
||||
case SqlDataType.NChar:
|
||||
case SqlDataType.VarChar:
|
||||
case SqlDataType.NVarChar:
|
||||
case SqlDataType.Binary:
|
||||
case SqlDataType.VarBinary:
|
||||
rv = string.Format(CultureInfo.InvariantCulture,
|
||||
"{0}({1})",
|
||||
dataType,
|
||||
type.MaximumLength);
|
||||
break;
|
||||
case SqlDataType.VarCharMax:
|
||||
case SqlDataType.NVarCharMax:
|
||||
case SqlDataType.VarBinaryMax:
|
||||
rv = string.Format(CultureInfo.InvariantCulture,
|
||||
"{0}(max)",
|
||||
dataType);
|
||||
break;
|
||||
// numeric and decimal are displayed as type precision,scale
|
||||
case SqlDataType.Numeric:
|
||||
case SqlDataType.Decimal:
|
||||
rv = string.Format(CultureInfo.InvariantCulture,
|
||||
"{0}({1},{2})",
|
||||
dataType,
|
||||
type.NumericPrecision,
|
||||
type.NumericScale);
|
||||
break;
|
||||
//time, datetimeoffset and datetime2 are displayed as type scale
|
||||
case SqlDataType.Time:
|
||||
case SqlDataType.DateTimeOffset:
|
||||
case SqlDataType.DateTime2:
|
||||
rv = string.Format(CultureInfo.InvariantCulture,
|
||||
"{0}({1})",
|
||||
dataType,
|
||||
type.NumericScale);
|
||||
break;
|
||||
// anything else is just type.
|
||||
case SqlDataType.Xml:
|
||||
if (type.Schema != null && type.Schema.Length > 0 && dataType != null && dataType.Length > 0)
|
||||
{
|
||||
rv = String.Format(CultureInfo.InvariantCulture
|
||||
, "xml ({0}{2}{1}.{0}{3}{1})"
|
||||
, LeftDelimiter
|
||||
, RightDelimiter
|
||||
, QuoteObjectName(type.Schema)
|
||||
, QuoteObjectName(dataType));
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = "xml";
|
||||
}
|
||||
break;
|
||||
case SqlDataType.UserDefinedDataType:
|
||||
case SqlDataType.UserDefinedTableType:
|
||||
case SqlDataType.UserDefinedType:
|
||||
//User defined types may be in a non-DBO schema so append it if necessary
|
||||
rv = GenerateSchemaQualifiedName(type.Schema, dataType, options.SchemaQualify);
|
||||
break;
|
||||
default:
|
||||
rv = dataType;
|
||||
break;
|
||||
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Double quotes certain characters in object name
|
||||
/// </summary>
|
||||
/// <param name="sqlObject"></param>
|
||||
public static string QuoteObjectName(string sqlObject)
|
||||
{
|
||||
|
||||
int len = sqlObject.Length;
|
||||
StringBuilder result = new StringBuilder(sqlObject.Length);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (sqlObject[i] == ']')
|
||||
{
|
||||
result.Append(']');
|
||||
}
|
||||
result.Append(sqlObject[i]);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static void WriteUseDatabase(Database parentObject, StringBuilder stringBuilder , ScriptingOptions options)
|
||||
{
|
||||
if (options.IncludeDatabaseContext)
|
||||
{
|
||||
string useDb = string.Format(CultureInfo.InvariantCulture, "USE {0}", CommonConstants.DefaultBatchSeperator);
|
||||
if (!options.NoCommandTerminator)
|
||||
{
|
||||
stringBuilder.Append(useDb);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(useDb);
|
||||
stringBuilder.Append(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetScript(ScriptingOptions options, StringCollection stringCollection)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (var item in stringCollection)
|
||||
{
|
||||
sb.Append(item);
|
||||
if (options != null && !options.NoCommandTerminator)
|
||||
{
|
||||
//Ensure the batch separator is always on a new line (to avoid syntax errors)
|
||||
//but don't write an extra if we already have one as this can affect definitions
|
||||
//of objects such as Stored Procedures (see TFS#9125366)
|
||||
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}{2}",
|
||||
item.EndsWith(Environment.NewLine) ? string.Empty : Environment.NewLine,
|
||||
CommonConstants.DefaultBatchSeperator,
|
||||
Environment.NewLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendFormat(CultureInfo.InvariantCulture, Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private UrnCollection CreateUrns(IDataSource dataSource)
|
||||
{
|
||||
IEnumerable<ScriptingObject> selectedObjects = new List<ScriptingObject>(this.Parameters.ScriptingObjects);
|
||||
|
||||
serverName = dataSource.ClusterName;
|
||||
databaseName = new SqlConnectionStringBuilder(this.Parameters.ConnectionString).InitialCatalog;
|
||||
UrnCollection urnCollection = new UrnCollection();
|
||||
foreach (var scriptingObject in selectedObjects)
|
||||
{
|
||||
if(string.IsNullOrEmpty(scriptingObject.Schema))
|
||||
{
|
||||
// TODO: get the default schema
|
||||
scriptingObject.Schema = "dbo";
|
||||
}
|
||||
urnCollection.Add(scriptingObject.ToUrn(serverName, databaseName));
|
||||
}
|
||||
return urnCollection;
|
||||
}
|
||||
|
||||
private void SetScriptBehavior(ScriptingOptions options)
|
||||
{
|
||||
// TODO: have to add Scripting behavior to Smo ScriptingOptions class
|
||||
// so it would support ScriptDropAndScreate
|
||||
switch (this.Parameters.ScriptOptions.ScriptCreateDrop)
|
||||
{
|
||||
case "ScriptCreate":
|
||||
options.ScriptDrops = false;
|
||||
break;
|
||||
case "ScriptDrop":
|
||||
options.ScriptDrops = true;
|
||||
break;
|
||||
default:
|
||||
options.ScriptDrops = false;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, SqlServerVersion> LoadScriptCompatibilityMap()
|
||||
{
|
||||
return new Dictionary<string, SqlServerVersion>
|
||||
{
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script150Compat.ToString(), SqlServerVersion.Version150},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script140Compat.ToString(), SqlServerVersion.Version140},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script130Compat.ToString(), SqlServerVersion.Version130},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script120Compat.ToString(), SqlServerVersion.Version120},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script110Compat.ToString(), SqlServerVersion.Version110},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script105Compat.ToString(), SqlServerVersion.Version105},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script100Compat.ToString(), SqlServerVersion.Version100},
|
||||
{SqlScriptOptions.ScriptCompatibilityOptions.Script90Compat.ToString(), SqlServerVersion.Version90}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void SetScriptingOptions(ScriptingOptions scriptingOptions)
|
||||
{
|
||||
scriptingOptions.AllowSystemObjects = true;
|
||||
|
||||
// setting this forces SMO to correctly script objects that have been renamed
|
||||
scriptingOptions.EnforceScriptingOptions = true;
|
||||
|
||||
//We always want role memberships for users and database roles to be scripted
|
||||
scriptingOptions.IncludeDatabaseRoleMemberships = true;
|
||||
SqlServerVersion targetServerVersion;
|
||||
if(scriptCompatibilityMap.TryGetValue(this.Parameters.ScriptOptions.ScriptCompatibilityOption, out targetServerVersion))
|
||||
{
|
||||
scriptingOptions.TargetServerVersion = targetServerVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
//If you are getting this assertion fail it means you are working for higher
|
||||
//version of SQL Server. You need to update this part of code.
|
||||
Logger.Write(TraceEventType.Warning, "This part of the code is not updated corresponding to latest version change");
|
||||
}
|
||||
|
||||
// for cloud scripting to work we also have to have Script Compat set to 105.
|
||||
// the defaults from scripting options should take care of it
|
||||
object targetDatabaseEngineType;
|
||||
if (Enum.TryParse(typeof(SqlScriptOptions.ScriptDatabaseEngineType), this.Parameters.ScriptOptions.TargetDatabaseEngineType, out targetDatabaseEngineType))
|
||||
{
|
||||
switch ((SqlScriptOptions.ScriptDatabaseEngineType)targetDatabaseEngineType)
|
||||
{
|
||||
case SqlScriptOptions.ScriptDatabaseEngineType.SingleInstance:
|
||||
scriptingOptions.TargetDatabaseEngineType = DatabaseEngineType.Standalone;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineType.SqlAzure:
|
||||
scriptingOptions.TargetDatabaseEngineType = DatabaseEngineType.SqlAzureDatabase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
object targetDatabaseEngineEdition;
|
||||
if (Enum.TryParse(typeof(SqlScriptOptions.ScriptDatabaseEngineEdition), this.Parameters.ScriptOptions.TargetDatabaseEngineEdition, out targetDatabaseEngineEdition))
|
||||
{
|
||||
switch ((SqlScriptOptions.ScriptDatabaseEngineEdition)targetDatabaseEngineEdition)
|
||||
{
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerPersonalEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Personal;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerStandardEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Standard;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerEnterpriseEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Enterprise;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerExpressEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Express;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlAzureDatabaseEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlDatabase;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlDatawarehouseEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlDataWarehouse;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerStretchEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlStretchDatabase;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerManagedInstanceEdition:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlManagedInstance;
|
||||
break;
|
||||
default:
|
||||
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Standard;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptingOptions.TargetDatabaseEngineEdition == DatabaseEngineEdition.SqlDataWarehouse)
|
||||
{
|
||||
scriptingOptions.Triggers = false;
|
||||
}
|
||||
|
||||
scriptingOptions.NoVardecimal = false; //making IncludeVarDecimal true for DPW
|
||||
|
||||
// scripting of stats is a combination of the Statistics
|
||||
// and the OptimizerData flag
|
||||
object scriptStatistics;
|
||||
if (Enum.TryParse(typeof(SqlScriptOptions.ScriptStatisticsOptions), this.Parameters.ScriptOptions.ScriptStatistics, out scriptStatistics))
|
||||
{
|
||||
switch ((SqlScriptOptions.ScriptStatisticsOptions)scriptStatistics)
|
||||
{
|
||||
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsAll:
|
||||
scriptingOptions.Statistics = true;
|
||||
scriptingOptions.OptimizerData = true;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsDDL:
|
||||
scriptingOptions.Statistics = true;
|
||||
scriptingOptions.OptimizerData = false;
|
||||
break;
|
||||
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsNone:
|
||||
scriptingOptions.Statistics = false;
|
||||
scriptingOptions.OptimizerData = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If Histogram and Update Statics are True then include DriIncludeSystemNames and AnsiPadding by default
|
||||
if (scriptingOptions.Statistics == true && scriptingOptions.OptimizerData == true)
|
||||
{
|
||||
scriptingOptions.DriIncludeSystemNames = true;
|
||||
scriptingOptions.AnsiPadding = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScripterScriptingError(object sender, ScriptingErrorEventArgs e)
|
||||
{
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending scripting error progress event, Urn={0}, OperationId={1}, Completed={2}, Error={3}",
|
||||
e.Current,
|
||||
this.OperationId,
|
||||
false,
|
||||
e?.InnerException?.ToString() ?? "null"));
|
||||
|
||||
this.SendProgressNotificationEvent(new ScriptingProgressNotificationParams
|
||||
{
|
||||
ScriptingObject = e.Current?.ToScriptingObject(),
|
||||
Status = "Failed",
|
||||
ErrorMessage = e?.InnerException?.Message,
|
||||
ErrorDetails = e?.InnerException?.ToString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/Microsoft.Kusto.ServiceLayer/Scripting/Scripter.cs
Normal file
75
src/Microsoft.Kusto.ServiceLayer/Scripting/Scripter.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// 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.Smo;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
internal partial class Scripter
|
||||
{
|
||||
private void Initialize()
|
||||
{
|
||||
// Instantiate the mapping dictionaries
|
||||
|
||||
// Mapping for supported type
|
||||
AddSupportedType(DeclarationType.Table, "Table", "table", typeof(Table));
|
||||
AddSupportedType(DeclarationType.View, "View", "view", typeof(View));
|
||||
AddSupportedType(DeclarationType.StoredProcedure, "StoredProcedure", "stored procedure", typeof(StoredProcedure));
|
||||
AddSupportedType(DeclarationType.Schema, "Schema", "schema", typeof(Schema));
|
||||
AddSupportedType(DeclarationType.UserDefinedDataType, "UserDefinedDataType", "user-defined data type", typeof(UserDefinedDataType));
|
||||
AddSupportedType(DeclarationType.UserDefinedTableType, "UserDefinedTableType", "user-defined table type", typeof(UserDefinedTableType));
|
||||
AddSupportedType(DeclarationType.Synonym, "Synonym", "", typeof(Synonym));
|
||||
AddSupportedType(DeclarationType.ScalarValuedFunction, "Function", "scalar-valued function", typeof(UserDefinedFunction));
|
||||
AddSupportedType(DeclarationType.TableValuedFunction, "Function", "table-valued function", typeof(UserDefinedFunction));
|
||||
|
||||
// Mapping for database engine edition
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Unknown, "SqlServerEnterpriseEdition"); //default case
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Personal, "SqlServerPersonalEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Standard, "SqlServerStandardEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Enterprise, "SqlServerEnterpriseEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Express, "SqlServerExpressEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlDatabase, "SqlAzureDatabaseEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlDataWarehouse, "SqlDatawarehouseEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlStretchDatabase, "SqlServerStretchEdition");
|
||||
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlManagedInstance, "SqlServerManagedInstance");
|
||||
|
||||
// Mapping for database engine type
|
||||
serverVersionMap.Add(9, "Script90Compat");
|
||||
serverVersionMap.Add(10, "Script100Compat");
|
||||
serverVersionMap.Add(11, "Script110Compat");
|
||||
serverVersionMap.Add(12, "Script120Compat");
|
||||
serverVersionMap.Add(13, "Script140Compat");
|
||||
serverVersionMap.Add(14, "Script140Compat");
|
||||
serverVersionMap.Add(15, "Script140Compat");
|
||||
|
||||
// Mapping the object types for scripting
|
||||
objectScriptMap.Add("table", "Table");
|
||||
objectScriptMap.Add("view", "View");
|
||||
objectScriptMap.Add("function", "Function");
|
||||
objectScriptMap.Add("storedprocedure", "Procedure");
|
||||
objectScriptMap.Add("userdefinedfunction", "Function");
|
||||
objectScriptMap.Add("tablevaluedfunction", "Function");
|
||||
objectScriptMap.Add("userdefineddatatype", "Type");
|
||||
objectScriptMap.Add("user", "User");
|
||||
objectScriptMap.Add("default", "Default");
|
||||
objectScriptMap.Add("rule", "Rule");
|
||||
objectScriptMap.Add("databaserole", "Role");
|
||||
objectScriptMap.Add("applicationrole", "Application Role");
|
||||
objectScriptMap.Add("sqlassembly", "Assembly");
|
||||
objectScriptMap.Add("ddltrigger", "Trigger");
|
||||
objectScriptMap.Add("synonym", "Synonym");
|
||||
objectScriptMap.Add("xmlschemacollection", "Xml Schema Collection");
|
||||
objectScriptMap.Add("schema", "Schema");
|
||||
objectScriptMap.Add("planguide", "sp_create_plan_guide");
|
||||
objectScriptMap.Add("userdefinedtype", "Type");
|
||||
objectScriptMap.Add("userdefinedaggregate", "Aggregate");
|
||||
objectScriptMap.Add("fulltextcatalog", "Fulltext Catalog");
|
||||
objectScriptMap.Add("userdefinedtabletype", "Type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
683
src/Microsoft.Kusto.ServiceLayer/Scripting/ScripterCore.cs
Normal file
683
src/Microsoft.Kusto.ServiceLayer/Scripting/ScripterCore.cs
Normal file
@@ -0,0 +1,683 @@
|
||||
//
|
||||
// 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 Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||
using Microsoft.Kusto.ServiceLayer.LanguageServices;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Location = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Location;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using System.Text;
|
||||
using System.Data;
|
||||
using Range = Microsoft.Kusto.ServiceLayer.Workspace.Contracts.Range;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
internal partial class Scripter
|
||||
{
|
||||
private bool error;
|
||||
private string errorMessage;
|
||||
private IDataSource DataSource { get; set; }
|
||||
private ConnectionInfo connectionInfo;
|
||||
private string tempPath;
|
||||
|
||||
// Dictionary that holds the object name (as appears on the TSQL create statement)
|
||||
private Dictionary<DeclarationType, string> sqlObjectTypes = new Dictionary<DeclarationType, string>();
|
||||
|
||||
private Dictionary<string, string> sqlObjectTypesFromQuickInfo = new Dictionary<string, string>();
|
||||
|
||||
private Dictionary<DatabaseEngineEdition, string> targetDatabaseEngineEditionMap = new Dictionary<DatabaseEngineEdition, string>();
|
||||
|
||||
private Dictionary<int, string> serverVersionMap = new Dictionary<int, string>();
|
||||
|
||||
private Dictionary<string, string> objectScriptMap = new Dictionary<string, string>();
|
||||
|
||||
internal Scripter() {}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a Peek Definition helper object
|
||||
/// </summary>
|
||||
/// <param name="dataSource">Data Source</param>
|
||||
internal Scripter(IDataSource dataSource, ConnectionInfo connInfo)
|
||||
{
|
||||
this.DataSource = dataSource;
|
||||
this.connectionInfo = connInfo;
|
||||
this.tempPath = FileUtilities.GetPeekDefinitionTempFolder();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the given type, scriptgetter and the typeName string to the respective dictionaries
|
||||
/// </summary>
|
||||
private void AddSupportedType(DeclarationType type, string typeName, string quickInfoType, Type smoObjectType)
|
||||
{
|
||||
sqlObjectTypes.Add(type, typeName);
|
||||
if (!string.IsNullOrEmpty(quickInfoType))
|
||||
{
|
||||
sqlObjectTypesFromQuickInfo.Add(quickInfoType.ToLowerInvariant(), typeName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the script of the selected token based on the type of the token
|
||||
/// </summary>
|
||||
/// <param name="declarationItems"></param>
|
||||
/// <param name="tokenText"></param>
|
||||
/// <param name="schemaName"></param>
|
||||
/// <returns>Location object of the script file</returns>
|
||||
internal DefinitionResult GetScript(ParseResult parseResult, Position position, IMetadataDisplayInfoProvider metadataDisplayInfoProvider, string tokenText, string schemaName)
|
||||
{
|
||||
int parserLine = position.Line;
|
||||
int parserColumn = position.Character;
|
||||
// Get DeclarationItems from The Intellisense Resolver for the selected token. The type of the selected token is extracted from the declarationItem.
|
||||
IEnumerable<Declaration> declarationItems = GetCompletionsForToken(parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
|
||||
if (declarationItems != null && declarationItems.Count() > 0)
|
||||
{
|
||||
foreach (Declaration declarationItem in declarationItems)
|
||||
{
|
||||
if (declarationItem.Title == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
StringComparison caseSensitivity = StringComparison.OrdinalIgnoreCase;
|
||||
// if declarationItem matches the selected token, script SMO using that type
|
||||
|
||||
if (declarationItem.Title.Equals(tokenText, caseSensitivity))
|
||||
{
|
||||
return GetDefinitionUsingDeclarationType(declarationItem.Type, declarationItem.DatabaseQualifiedName, tokenText, schemaName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if no declarationItem matched the selected token, we try to find the type of the token using QuickInfo.Text
|
||||
string quickInfoText = GetQuickInfoForToken(parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
|
||||
return GetDefinitionUsingQuickInfoText(quickInfoText, tokenText, schemaName);
|
||||
}
|
||||
// no definition found
|
||||
return GetDefinitionErrorResult(SR.PeekDefinitionNoResultsError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script an object using the type extracted from quickInfo Text
|
||||
/// </summary>
|
||||
/// <param name="quickInfoText">the text from the quickInfo for the selected token</param>
|
||||
/// <param name="tokenText">The text of the selected token</param>
|
||||
/// <param name="schemaName">Schema name</param>
|
||||
/// <returns></returns>
|
||||
internal DefinitionResult GetDefinitionUsingQuickInfoText(string quickInfoText, string tokenText, string schemaName)
|
||||
{
|
||||
StringComparison caseSensitivity = StringComparison.OrdinalIgnoreCase;
|
||||
string tokenType = GetTokenTypeFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
|
||||
if (tokenType != null)
|
||||
{
|
||||
if (sqlObjectTypesFromQuickInfo.ContainsKey(tokenType.ToLowerInvariant()))
|
||||
{
|
||||
// With SqlLogin authentication, the defaultSchema property throws an Exception when accessed.
|
||||
// This workaround ensures that a schema name is present by attempting
|
||||
// to get the schema name from the declaration item.
|
||||
// If all fails, the default schema name is assumed to be "dbo"
|
||||
if ((connectionInfo != null && connectionInfo.ConnectionDetails.AuthenticationType.Equals(Constants.SqlLoginAuthenticationType)) && string.IsNullOrEmpty(schemaName))
|
||||
{
|
||||
string fullObjectName = this.GetFullObjectNameFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
|
||||
schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
|
||||
}
|
||||
Location[] locations = GetSqlObjectDefinition(
|
||||
tokenText,
|
||||
schemaName,
|
||||
sqlObjectTypesFromQuickInfo[tokenType.ToLowerInvariant()]
|
||||
);
|
||||
DefinitionResult result = new DefinitionResult
|
||||
{
|
||||
IsErrorResult = this.error,
|
||||
Message = this.errorMessage,
|
||||
Locations = locations
|
||||
};
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a type was found but is not in sqlScriptGettersFromQuickInfo, then the type is not supported
|
||||
return GetDefinitionErrorResult(SR.PeekDefinitionTypeNotSupportedError);
|
||||
}
|
||||
}
|
||||
// no definition found
|
||||
return GetDefinitionErrorResult(SR.PeekDefinitionNoResultsError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script a object using the type extracted from declarationItem
|
||||
/// </summary>
|
||||
/// <param name="declarationItem">The Declaration object that matched with the selected token</param>
|
||||
/// <param name="tokenText">The text of the selected token</param>
|
||||
/// <param name="schemaName">Schema name</param>
|
||||
/// <returns></returns>
|
||||
internal DefinitionResult GetDefinitionUsingDeclarationType(DeclarationType type, string databaseQualifiedName, string tokenText, string schemaName)
|
||||
{
|
||||
if (sqlObjectTypes.ContainsKey(type))
|
||||
{
|
||||
// With SqlLogin authentication, the defaultSchema property throws an Exception when accessed.
|
||||
// This workaround ensures that a schema name is present by attempting
|
||||
// to get the schema name from the declaration item.
|
||||
// If all fails, the default schema name is assumed to be "dbo"
|
||||
if ((connectionInfo != null && connectionInfo.ConnectionDetails.AuthenticationType.Equals(Constants.SqlLoginAuthenticationType)) && string.IsNullOrEmpty(schemaName))
|
||||
{
|
||||
string fullObjectName = databaseQualifiedName;
|
||||
schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
|
||||
}
|
||||
Location[] locations = GetSqlObjectDefinition(
|
||||
tokenText,
|
||||
schemaName,
|
||||
sqlObjectTypes[type]
|
||||
);
|
||||
DefinitionResult result = new DefinitionResult
|
||||
{
|
||||
IsErrorResult = this.error,
|
||||
Message = this.errorMessage,
|
||||
Locations = locations
|
||||
};
|
||||
return result;
|
||||
}
|
||||
// If a type was found but is not in sqlScriptGetters, then the type is not supported
|
||||
return GetDefinitionErrorResult(SR.PeekDefinitionTypeNotSupportedError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Script a object using SMO and write to a file.
|
||||
/// </summary>
|
||||
/// <param name="sqlScriptGetter">Function that returns the SMO scripts for an object</param>
|
||||
/// <param name="objectName">SQL object name</param>
|
||||
/// <param name="schemaName">Schema name or null</param>
|
||||
/// <param name="objectType">Type of SQL object</param>
|
||||
/// <returns>Location object representing URI and range of the script file</returns>
|
||||
internal Location[] GetSqlObjectDefinition(
|
||||
string objectName,
|
||||
string schemaName,
|
||||
string objectType)
|
||||
{
|
||||
// script file destination
|
||||
string tempFileName = (schemaName != null) ? Path.Combine(this.tempPath, string.Format("{0}.{1}.sql", schemaName, objectName))
|
||||
: Path.Combine(this.tempPath, string.Format("{0}.sql", objectName));
|
||||
|
||||
SmoScriptingOperation operation = InitScriptOperation(objectName, schemaName, objectType);
|
||||
operation.Execute();
|
||||
string script = operation.ScriptText;
|
||||
|
||||
bool objectFound = false;
|
||||
int createStatementLineNumber = 0;
|
||||
|
||||
File.WriteAllText(tempFileName, script);
|
||||
string[] lines = File.ReadAllLines(tempFileName);
|
||||
int lineCount = 0;
|
||||
string createSyntax = null;
|
||||
if (objectScriptMap.ContainsKey(objectType.ToLower()))
|
||||
{
|
||||
createSyntax = string.Format("CREATE");
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (LineContainsObject(line, objectName, createSyntax))
|
||||
{
|
||||
createStatementLineNumber = lineCount;
|
||||
objectFound = true;
|
||||
break;
|
||||
}
|
||||
lineCount++;
|
||||
}
|
||||
}
|
||||
if (objectFound)
|
||||
{
|
||||
Location[] locations = GetLocationFromFile(tempFileName, createStatementLineNumber);
|
||||
return locations;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.error = true;
|
||||
this.errorMessage = SR.PeekDefinitionNoResultsError;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
/// <summary>
|
||||
/// Return schema name from the full name of the database. If schema is missing return dbo as schema name.
|
||||
/// </summary>
|
||||
/// <param name="fullObjectName"> The full database qualified name(database.schema.object)</param>
|
||||
/// <param name="objectName"> Object name</param>
|
||||
/// <returns>Schema name</returns>
|
||||
internal string GetSchemaFromDatabaseQualifiedName(string fullObjectName, string objectName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fullObjectName))
|
||||
{
|
||||
string[] tokens = fullObjectName.Split('.');
|
||||
for (int i = tokens.Length - 1; i > 0; i--)
|
||||
{
|
||||
if (tokens[i].Equals(objectName))
|
||||
{
|
||||
return tokens[i - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return "dbo";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a file to a location array containing a location object as expected by the extension
|
||||
/// </summary>
|
||||
internal Location[] GetLocationFromFile(string tempFileName, int lineNumber)
|
||||
{
|
||||
// Get absolute Uri based on uri format. This works around a dotnetcore URI bug for linux paths.
|
||||
if (Path.DirectorySeparatorChar.Equals('/'))
|
||||
{
|
||||
tempFileName = "file:" + tempFileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempFileName = new Uri(tempFileName).AbsoluteUri;
|
||||
}
|
||||
// Create a location array containing the tempFile Uri, as expected by VSCode.
|
||||
Location[] locations = new[]
|
||||
{
|
||||
new Location
|
||||
{
|
||||
Uri = tempFileName,
|
||||
Range = new Range
|
||||
{
|
||||
Start = new Position { Line = lineNumber, Character = 0},
|
||||
End = new Position { Line = lineNumber + 1, Character = 0}
|
||||
}
|
||||
}
|
||||
};
|
||||
return locations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create definition error result object
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">Error message</param>
|
||||
/// <returns> DefinitionResult</returns>
|
||||
internal DefinitionResult GetDefinitionErrorResult(string errorMessage)
|
||||
{
|
||||
return new DefinitionResult
|
||||
{
|
||||
IsErrorResult = true,
|
||||
Message = errorMessage,
|
||||
Locations = null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return full object name(database.schema.objectName) from the quickInfo text("type database.schema.objectName")
|
||||
/// </summary>
|
||||
/// <param name="quickInfoText">QuickInfo Text for this token</param>
|
||||
/// <param name="tokenText">Token Text</param>
|
||||
/// <param name="caseSensitivity">StringComparison enum</param>
|
||||
/// <returns></returns>
|
||||
internal string GetFullObjectNameFromQuickInfo(string quickInfoText, string tokenText, StringComparison caseSensitivity)
|
||||
{
|
||||
if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// extract full object name from quickInfo text
|
||||
string[] tokens = quickInfoText.Split(' ');
|
||||
List<string> tokenList = tokens.Where(el => el.IndexOf(tokenText, caseSensitivity) >= 0).ToList();
|
||||
return (tokenList?.Count() > 0) ? tokenList[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return token type from the quickInfo text("type database.schema.objectName")
|
||||
/// </summary>
|
||||
/// <param name="quickInfoText">QuickInfo Text for this token</param>
|
||||
/// <param name="tokenText"Token Text></param>
|
||||
/// <param name="caseSensitivity">StringComparison enum</param>
|
||||
/// <returns></returns>
|
||||
internal string GetTokenTypeFromQuickInfo(string quickInfoText, string tokenText, StringComparison caseSensitivity)
|
||||
{
|
||||
if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// extract string denoting the token type from quickInfo text
|
||||
string[] tokens = quickInfoText.Split(' ');
|
||||
List<int> indexList = tokens.Select((s, i) => new { i, s }).Where(el => (el.s).IndexOf(tokenText, caseSensitivity) >= 0).Select(el => el.i).ToList();
|
||||
return (indexList?.Count() > 0) ? String.Join(" ", tokens.Take(indexList[0])) : null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper method that calls Resolver.GetQuickInfo
|
||||
/// </summary>
|
||||
internal string GetQuickInfoForToken(ParseResult parseResult, int parserLine, int parserColumn, IMetadataDisplayInfoProvider metadataDisplayInfoProvider)
|
||||
{
|
||||
if (parseResult == null || metadataDisplayInfoProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Babel.CodeObjectQuickInfo quickInfo = Resolver.GetQuickInfo(
|
||||
parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
|
||||
return quickInfo?.Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper method that calls Resolver.FindCompletions
|
||||
/// </summary>
|
||||
/// <param name="parseResult"></param>
|
||||
/// <param name="parserLine"></param>
|
||||
/// <param name="parserColumn"></param>
|
||||
/// <param name="metadataDisplayInfoProvider"></param>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<Declaration> GetCompletionsForToken(ParseResult parseResult, int parserLine, int parserColumn, IMetadataDisplayInfoProvider metadataDisplayInfoProvider)
|
||||
{
|
||||
if (parseResult == null || metadataDisplayInfoProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Resolver.FindCompletions(
|
||||
parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper method that calls Resolver.FindCompletions
|
||||
/// </summary>
|
||||
/// <param name="objectName"></param>
|
||||
/// <param name="schemaName"></param>
|
||||
/// <param name="objectType"></param>
|
||||
/// <param name="tempFileName"></param>
|
||||
/// <returns></returns>
|
||||
internal SmoScriptingOperation InitScriptOperation(string objectName, string schemaName, string objectType)
|
||||
{
|
||||
// object that has to be scripted
|
||||
ScriptingObject scriptingObject = new ScriptingObject
|
||||
{
|
||||
Name = objectName,
|
||||
Schema = schemaName,
|
||||
Type = objectType
|
||||
};
|
||||
|
||||
// scripting options
|
||||
ScriptOptions options = new ScriptOptions
|
||||
{
|
||||
ScriptCreateDrop = "ScriptCreate",
|
||||
TypeOfDataToScript = "SchemaOnly",
|
||||
ScriptStatistics = "ScriptStatsNone",
|
||||
ScriptExtendedProperties = false,
|
||||
ScriptUseDatabase = false,
|
||||
IncludeIfNotExists = false,
|
||||
GenerateScriptForDependentObjects = false,
|
||||
IncludeDescriptiveHeaders = false,
|
||||
ScriptCheckConstraints = false,
|
||||
ScriptChangeTracking = false,
|
||||
ScriptDataCompressionOptions = false,
|
||||
ScriptForeignKeys = false,
|
||||
ScriptFullTextIndexes = false,
|
||||
ScriptIndexes = false,
|
||||
ScriptPrimaryKeys = false,
|
||||
ScriptTriggers = false,
|
||||
UniqueKeys = false
|
||||
|
||||
};
|
||||
|
||||
List<ScriptingObject> objectList = new List<ScriptingObject>();
|
||||
objectList.Add(scriptingObject);
|
||||
|
||||
// create parameters for the scripting operation
|
||||
|
||||
ScriptingParams parameters = new ScriptingParams
|
||||
{
|
||||
ConnectionString = ConnectionService.BuildConnectionString(this.connectionInfo.ConnectionDetails),
|
||||
ScriptingObjects = objectList,
|
||||
ScriptOptions = options,
|
||||
ScriptDestination = "ToEditor"
|
||||
};
|
||||
|
||||
return new ScriptAsScriptingOperation(parameters, DataSource);
|
||||
}
|
||||
|
||||
internal bool LineContainsObject(string line, string objectName, string createSyntax)
|
||||
{
|
||||
if (line.IndexOf(createSyntax, StringComparison.OrdinalIgnoreCase) >= 0 &&
|
||||
line.IndexOf(objectName, StringComparison.OrdinalIgnoreCase) >=0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static class ScriptingGlobals
|
||||
{
|
||||
/// <summary>
|
||||
/// Left delimiter for an named object
|
||||
/// </summary>
|
||||
public const char LeftDelimiter = '[';
|
||||
|
||||
/// <summary>
|
||||
/// right delimiter for a named object
|
||||
/// </summary>
|
||||
public const char RightDelimiter = ']';
|
||||
}
|
||||
|
||||
internal static class ScriptingUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Quote the name of a given sql object.
|
||||
/// </summary>
|
||||
/// <param name="sqlObject">object</param>
|
||||
/// <returns>quoted object name</returns>
|
||||
internal static string QuoteObjectName(string sqlObject)
|
||||
{
|
||||
return QuoteObjectName(sqlObject, ']');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quotes the name of a given sql object
|
||||
/// </summary>
|
||||
/// <param name="sqlObject">object</param>
|
||||
/// <param name="quote">quote to use</param>
|
||||
/// <returns></returns>
|
||||
internal static string QuoteObjectName(string sqlObject, char quote)
|
||||
{
|
||||
int len = sqlObject.Length;
|
||||
StringBuilder result = new StringBuilder(sqlObject.Length);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (sqlObject[i] == quote)
|
||||
{
|
||||
result.Append(quote);
|
||||
}
|
||||
result.Append(sqlObject[i]);
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
internal static string SelectAllValuesFromTransmissionQueue(Urn urn)
|
||||
{
|
||||
string script = string.Empty;
|
||||
StringBuilder selectQuery = new StringBuilder();
|
||||
|
||||
/*
|
||||
SELECT TOP *, casted_message_body =
|
||||
CASE MESSAGE_TYPE_NAME WHEN 'X'
|
||||
THEN CAST(MESSAGE_BODY AS NVARCHAR(MAX))
|
||||
ELSE MESSAGE_BODY
|
||||
END
|
||||
FROM [new].[sys].[transmission_queue]
|
||||
*/
|
||||
selectQuery.Append("SELECT TOP (1000) ");
|
||||
selectQuery.Append("*, casted_message_body = \r\nCASE message_type_name WHEN 'X' \r\n THEN CAST(message_body AS NVARCHAR(MAX)) \r\n ELSE message_body \r\nEND \r\n");
|
||||
|
||||
// from clause
|
||||
selectQuery.Append("FROM ");
|
||||
Urn dbUrn = urn;
|
||||
|
||||
// database
|
||||
while (dbUrn.Parent != null && dbUrn.Type != "Database")
|
||||
{
|
||||
dbUrn = dbUrn.Parent;
|
||||
}
|
||||
selectQuery.AppendFormat("{0}{1}{2}",
|
||||
ScriptingGlobals.LeftDelimiter,
|
||||
ScriptingUtils.QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
|
||||
ScriptingGlobals.RightDelimiter);
|
||||
//SYS
|
||||
selectQuery.AppendFormat(".{0}sys{1}",
|
||||
ScriptingGlobals.LeftDelimiter,
|
||||
ScriptingGlobals.RightDelimiter);
|
||||
//TRANSMISSION QUEUE
|
||||
selectQuery.AppendFormat(".{0}transmission_queue{1}",
|
||||
ScriptingGlobals.LeftDelimiter,
|
||||
ScriptingGlobals.RightDelimiter);
|
||||
|
||||
script = selectQuery.ToString();
|
||||
return script;
|
||||
}
|
||||
|
||||
internal static string SelectAllValues(Urn urn)
|
||||
{
|
||||
string script = string.Empty;
|
||||
StringBuilder selectQuery = new StringBuilder();
|
||||
selectQuery.Append("SELECT TOP (1000) ");
|
||||
selectQuery.Append("*, casted_message_body = \r\nCASE message_type_name WHEN 'X' \r\n THEN CAST(message_body AS NVARCHAR(MAX)) \r\n ELSE message_body \r\nEND \r\n");
|
||||
|
||||
// from clause
|
||||
selectQuery.Append("FROM ");
|
||||
Urn dbUrn = urn;
|
||||
|
||||
// database
|
||||
while (dbUrn.Parent != null && dbUrn.Type != "Database")
|
||||
{
|
||||
dbUrn = dbUrn.Parent;
|
||||
}
|
||||
selectQuery.AppendFormat("{0}{1}{2}",
|
||||
ScriptingGlobals.LeftDelimiter,
|
||||
ScriptingUtils.QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
|
||||
ScriptingGlobals.RightDelimiter);
|
||||
// schema
|
||||
selectQuery.AppendFormat(".{0}{1}{2}",
|
||||
ScriptingGlobals.LeftDelimiter,
|
||||
ScriptingUtils.QuoteObjectName(urn.GetAttribute("Schema"), ScriptingGlobals.RightDelimiter),
|
||||
ScriptingGlobals.RightDelimiter);
|
||||
// object
|
||||
selectQuery.AppendFormat(".{0}{1}{2}",
|
||||
ScriptingGlobals.LeftDelimiter,
|
||||
ScriptingUtils.QuoteObjectName(urn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
|
||||
ScriptingGlobals.RightDelimiter);
|
||||
|
||||
//Adding no lock in the end.
|
||||
selectQuery.AppendFormat(" WITH(NOLOCK)");
|
||||
|
||||
script = selectQuery.ToString();
|
||||
return script;
|
||||
}
|
||||
|
||||
internal DataTable GetColumnNames(Server server, Urn urn, bool isDw)
|
||||
{
|
||||
List<string> filterExpressions = new List<string>();
|
||||
if (server.Version.Major >= 10)
|
||||
{
|
||||
// We don't have to include sparce columns as all the sparce columns data.
|
||||
// Can be obtain from column set columns.
|
||||
filterExpressions.Add("@IsSparse=0");
|
||||
}
|
||||
|
||||
// Check if we're called for EDIT for SQL2016+/Sterling+.
|
||||
// We need to omit temporal columns if such are present on this table.
|
||||
if (server.Version.Major >= 13 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && server.Version.Major >= 12))
|
||||
{
|
||||
// We're called in order to generate a list of columns for EDIT TOP N rows.
|
||||
// Don't return auto-generated, auto-populated, read-only temporal columns.
|
||||
filterExpressions.Add("@GeneratedAlwaysType=0");
|
||||
}
|
||||
|
||||
// Check if we're called for SQL2017/Sterling+.
|
||||
// We need to omit graph internal columns if such are present on this table.
|
||||
if (server.Version.Major >= 14 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && !isDw))
|
||||
{
|
||||
// from Smo.GraphType:
|
||||
// 0 = None
|
||||
// 1 = GraphId
|
||||
// 2 = GraphIdComputed
|
||||
// 3 = GraphFromId
|
||||
// 4 = GraphFromObjId
|
||||
// 5 = GraphFromIdComputed
|
||||
// 6 = GraphToId
|
||||
// 7 = GraphToObjId
|
||||
// 8 = GraphToIdComputed
|
||||
//
|
||||
// We only want to show types 0, 2, 5, and 8:
|
||||
filterExpressions.Add("(@GraphType=0 or @GraphType=2 or @GraphType=5 or @GraphType=8)");
|
||||
}
|
||||
|
||||
Request request = new Request();
|
||||
// If we have any filters on the columns, add them.
|
||||
if (filterExpressions.Count > 0)
|
||||
{
|
||||
request.Urn = String.Format("{0}/Column[{1}]", urn.ToString(), string.Join(" and ", filterExpressions.ToArray()));
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Urn = String.Format("{0}/Column", urn.ToString());
|
||||
}
|
||||
|
||||
request.Fields = new String[] { "Name" };
|
||||
|
||||
// get the columns in the order they were created
|
||||
OrderBy order = new OrderBy();
|
||||
order.Dir = OrderBy.Direction.Asc;
|
||||
order.Field = "ID";
|
||||
request.OrderByList = new OrderBy[] { order };
|
||||
|
||||
Enumerator en = new Enumerator();
|
||||
|
||||
// perform the query.
|
||||
DataTable dt = null;
|
||||
EnumResult result = en.Process(server.ConnectionContext, request);
|
||||
|
||||
if (result.Type == ResultType.DataTable)
|
||||
{
|
||||
dt = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
dt = ((DataSet)result).Tables[0];
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
|
||||
internal string SelectFromTableOrView(IDataSource dataSource, Urn urn)
|
||||
{
|
||||
StringBuilder selectQuery = new StringBuilder();
|
||||
|
||||
// TODOKusto: Can we combine this with snippets. All queries generated here could also be snippets.
|
||||
// TODOKusto: Extract into the Kusto folder.
|
||||
selectQuery.Append($"{KustoQueryUtils.EscapeName(urn.GetAttribute("Name"))}");
|
||||
selectQuery.Append($"{KustoQueryUtils.StatementSeparator}");
|
||||
selectQuery.Append("limit 1000");
|
||||
|
||||
return selectQuery.ToString();
|
||||
}
|
||||
|
||||
internal string AlterFunction(IDataSource dataSource, ScriptingObject scriptingObject)
|
||||
{
|
||||
var functionName = scriptingObject.Name.Substring(0, scriptingObject.Name.IndexOf('('));
|
||||
return dataSource.GenerateAlterFunctionScript(functionName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.SqlScriptPublish;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods used by the scripting service.
|
||||
/// </summary>
|
||||
internal static class ScriptingExtensionMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the status of a scripting operation for the passed scripting event.
|
||||
/// </summary>
|
||||
/// <param name="e">The scripting event.</param>
|
||||
/// <returns>The status.</returns>
|
||||
public static string GetStatus(this ScriptEventArgs e)
|
||||
{
|
||||
Validate.IsNotNull("e", e);
|
||||
|
||||
string status = null;
|
||||
|
||||
if (e.Error != null)
|
||||
{
|
||||
status = "Error";
|
||||
}
|
||||
else if (e.Completed)
|
||||
{
|
||||
status = "Completed";
|
||||
}
|
||||
else
|
||||
{
|
||||
status = "Progress";
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of ScriptingObject instances for the passed SqlScriptPublishModel instance.
|
||||
/// </summary>
|
||||
/// <param name="publishModel">The sql script publish model instance.</param>
|
||||
/// <returns>The list of scripting objects.</returns>
|
||||
public static List<ScriptingObject> GetDatabaseObjects(this SqlScriptPublishModel publishModel)
|
||||
{
|
||||
Validate.IsNotNull("publishModel", publishModel);
|
||||
|
||||
List<ScriptingObject> databaseObjects = new List<ScriptingObject>();
|
||||
|
||||
IEnumerable<DatabaseObjectType> objectTypes = publishModel.GetDatabaseObjectTypes();
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Loaded SMO object type count {0}, types: {1}",
|
||||
objectTypes.Count(),
|
||||
string.Join(", ", objectTypes)));
|
||||
|
||||
foreach (DatabaseObjectType objectType in objectTypes)
|
||||
{
|
||||
IEnumerable<KeyValuePair<string, string>> databaseObjectsOfType = publishModel.EnumChildrenForDatabaseObjectType(objectType);
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Loaded SMO urn object count {0} for type {1}, urns: {2}",
|
||||
objectType,
|
||||
databaseObjectsOfType.Count(),
|
||||
string.Join(", ", databaseObjectsOfType.Select(p => p.Value))));
|
||||
|
||||
databaseObjects.AddRange(databaseObjectsOfType.Select(d => new Urn(d.Value).ToScriptingObject()));
|
||||
}
|
||||
|
||||
return databaseObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SMO Urn instance based on the passed ScriptingObject instance.
|
||||
/// </summary>
|
||||
/// <param name="scriptingObject">The scripting object instance.</param>
|
||||
/// <param name="database">The name of the database referenced by the Urn.</param>
|
||||
/// <returns>The Urn instance.</returns>
|
||||
public static Urn ToUrn(this ScriptingObject scriptingObject, string server, string database)
|
||||
{
|
||||
Validate.IsNotNull("scriptingObject", scriptingObject);
|
||||
Validate.IsNotNullOrEmptyString("server", server);
|
||||
|
||||
Validate.IsNotNullOrWhitespaceString("scriptingObject.Name", scriptingObject.Name);
|
||||
Validate.IsNotNullOrWhitespaceString("scriptingObject.Type", scriptingObject.Type);
|
||||
|
||||
// Leaving the server name blank will automatically match whatever the server SMO is running against.
|
||||
string urn = string.Format(
|
||||
"Server[@Name='{0}']/Database[@Name='{1}']/{2}[@Name='{3}' {4}]",
|
||||
server.ToUpper(),
|
||||
Urn.EscapeString(database),
|
||||
scriptingObject.Type,
|
||||
Urn.EscapeString(scriptingObject.Name),
|
||||
scriptingObject.Schema != null ? string.Format("and @Schema = '{0}'", Urn.EscapeString(scriptingObject.Schema)) : string.Empty);
|
||||
|
||||
return new Urn(urn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ScriptingObject instance based on the passed SMO Urn instance.
|
||||
/// </summary>
|
||||
/// <param name="urn">The urn instance.</param>
|
||||
/// <returns>The scripting object instance.</returns>
|
||||
public static ScriptingObject ToScriptingObject(this Urn urn)
|
||||
{
|
||||
Validate.IsNotNull("urn", urn);
|
||||
|
||||
return new ScriptingObject
|
||||
{
|
||||
Type = urn.Type,
|
||||
Schema = urn.GetAttribute("Schema"),
|
||||
Name = urn.GetAttribute("Name"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// 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.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.SqlScriptPublish;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to represent an in-progress list objects operation.
|
||||
/// </summary>
|
||||
public sealed class ScriptingListObjectsOperation : ScriptingOperation
|
||||
{
|
||||
private bool disposed = false;
|
||||
|
||||
public ScriptingListObjectsOperation(ScriptingListObjectsParams parameters)
|
||||
{
|
||||
Validate.IsNotNull("parameters", parameters);
|
||||
|
||||
this.Parameters = parameters;
|
||||
}
|
||||
|
||||
private ScriptingListObjectsParams Parameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a the list object operation is complete.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An event can be completed by the following conditions: success, cancel, error.
|
||||
/// </remarks>
|
||||
public event EventHandler<ScriptingListObjectsCompleteParams> CompleteNotification;
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
if (this.CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw new OperationCanceledException(this.CancellationToken);
|
||||
}
|
||||
|
||||
SqlScriptPublishModel publishModel = null;
|
||||
|
||||
try
|
||||
{
|
||||
this.ValidateScriptDatabaseParams();
|
||||
|
||||
publishModel = new SqlScriptPublishModel(this.Parameters.ConnectionString);
|
||||
List<ScriptingObject> databaseObjects = publishModel.GetDatabaseObjects();
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending list object completion notification count {0}, objects: {1}",
|
||||
databaseObjects,
|
||||
string.Join(", ", databaseObjects)));
|
||||
|
||||
this.SendCompletionNotificationEvent(new ScriptingListObjectsCompleteParams
|
||||
{
|
||||
OperationId = this.OperationId,
|
||||
ScriptingObjects = databaseObjects,
|
||||
Count = databaseObjects.Count,
|
||||
Success = true,
|
||||
SequenceNumber = 1,
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Write(TraceEventType.Information, string.Format("Scripting operation {0} was canceled", this.OperationId));
|
||||
if (e.IsOperationCanceledException())
|
||||
{
|
||||
this.SendCompletionNotificationEvent(new ScriptingListObjectsCompleteParams
|
||||
{
|
||||
OperationId = this.OperationId,
|
||||
Canceled = true,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Write(TraceEventType.Error, string.Format("Scripting operation {0} failed with exception {1}", this.OperationId, e));
|
||||
this.SendCompletionNotificationEvent(new ScriptingListObjectsCompleteParams
|
||||
{
|
||||
OperationId = this.OperationId,
|
||||
HasError = true,
|
||||
ErrorMessage = e.Message,
|
||||
ErrorDetails = e.ToString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendCompletionNotificationEvent(ScriptingListObjectsCompleteParams parameters)
|
||||
{
|
||||
this.CompleteNotification?.Invoke(this, parameters);
|
||||
}
|
||||
|
||||
private void ValidateScriptDatabaseParams()
|
||||
{
|
||||
try
|
||||
{
|
||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(this.Parameters.ConnectionString);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ArgumentException(SR.ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the scripting operation.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
this.Cancel();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements matching logic to filter scripting objects based on an
|
||||
/// include/exclude criteria.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// First, objects are included by the include filter. Then, objects are removed by
|
||||
/// the exclude filter. Matches are made by comparing case insensitive strings for the
|
||||
/// ScriptingObject Type, Schema, and Name properties. Wildcards '*' are supported for
|
||||
/// the ScriptingObject Schema and Name properties. Matching on ScriptingObject Type
|
||||
/// property must be an exact match.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// Include ScriptingObject { Type = null, Schema = "dbo", Name = null }
|
||||
/// -> matches all objects in the dbo schema.
|
||||
///
|
||||
/// Include ScriptingObject { Type = "Table", Schema = "dbo", Name = null }
|
||||
/// -> matches all tables in the dbo schema.
|
||||
///
|
||||
/// Include ScriptingObject { Type = "Table", Schema = null, Name = "Emp*" }
|
||||
/// -> matches all table names that start with "Emp"
|
||||
///
|
||||
/// Include ScriptingObject { Type = "View", Schema = null, Name = "Emp*" }
|
||||
/// Include ScriptingObject { Type = "Table", Schema = null, Name = "Emp*" }
|
||||
/// -> matches all table and views with names that start with "Emp"
|
||||
///
|
||||
/// Include ScriptingObject { Type = "Table", Schema = null, Name = null }
|
||||
/// Exclude ScriptingObject { Type = null, Schema = "HumanResources", Name = null }
|
||||
/// -> matches all tables except tables in the "HumanResources" schema
|
||||
///
|
||||
/// </remarks>
|
||||
public static class ScriptingObjectMatcher
|
||||
{
|
||||
private const string Wildcard = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Given a collection of candidate scripting objects, filters the items that match
|
||||
/// based on the passed include and exclude criteria.
|
||||
/// </summary>
|
||||
/// <param name="includeCriteria">The include object criteria.</param>
|
||||
/// <param name="excludeCriteria">The exclude object criteria.</param>
|
||||
/// <param name="includeSchemas">The include schema filter.</param>
|
||||
/// <param name="excludeSchemas">The exclude schema filter.</param>
|
||||
/// <param name="includeTypes">The include type filter.</param>
|
||||
/// <param name="excludeTypes">The exclude type filter.</param>
|
||||
/// <param name="candidates">The candidate object to filter.</param>
|
||||
/// <returns>The matching scripting objects.</returns>
|
||||
public static IEnumerable<ScriptingObject> Match(
|
||||
ScriptingObject includeCriteria,
|
||||
ScriptingObject excludeCriteria,
|
||||
string includeSchemas,
|
||||
string excludeSchemas,
|
||||
string includeTypes,
|
||||
string excludeTypes,
|
||||
IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
return Match(
|
||||
includeCriteria == null ? new ScriptingObject[0] : new[] { includeCriteria },
|
||||
excludeCriteria == null ? new ScriptingObject[0] : new[] { excludeCriteria },
|
||||
includeSchemas == null ? new List<string>(): new List<string> { includeSchemas },
|
||||
excludeSchemas == null ? new List<string>(): new List<string> { excludeSchemas },
|
||||
includeTypes == null ? new List<string>(): new List<string> { includeTypes },
|
||||
excludeTypes == null ? new List<string>(): new List<string> { excludeTypes },
|
||||
candidates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a collection of candidate scripting objects, filters the items that match
|
||||
/// based on the passed include and exclude criteria.
|
||||
/// </summary>
|
||||
/// <param name="includeCriteria">The collection of include object criteria items.</param>
|
||||
/// <param name="excludeCriteria">The collection of exclude object criteria items.</param>
|
||||
/// <param name="includeSchemas">The collection of include schema items.</param>
|
||||
/// <param name="excludeSchemas">The collection of exclude schema items.</param>
|
||||
/// <param name="includeTypes">The collection of include type items.</param>
|
||||
/// <param name="excludeTypes">The collection of exclude type items.</param>
|
||||
/// <param name="candidates">The candidate object to filter.</param>
|
||||
/// <returns>The matching scripting objects.</returns>
|
||||
public static IEnumerable<ScriptingObject> Match(
|
||||
IEnumerable<ScriptingObject> includeCriteria,
|
||||
IEnumerable<ScriptingObject> excludeCriteria,
|
||||
IEnumerable<string> includeSchemas,
|
||||
IEnumerable<string> excludeSchemas,
|
||||
IEnumerable<string> includeTypes,
|
||||
IEnumerable<string> excludeTypes,
|
||||
IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
Validate.IsNotNull("candidates", candidates);
|
||||
|
||||
IEnumerable<ScriptingObject> matchedObjects = new List<ScriptingObject>();
|
||||
|
||||
if (includeCriteria != null && includeCriteria.Any())
|
||||
{
|
||||
foreach (ScriptingObject scriptingObjectCriteria in includeCriteria)
|
||||
{
|
||||
IEnumerable<ScriptingObject> matches = MatchCriteria(scriptingObjectCriteria, candidates);
|
||||
matchedObjects = matchedObjects.Union(matches);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedObjects = candidates;
|
||||
}
|
||||
|
||||
if (excludeCriteria != null && excludeCriteria.Any())
|
||||
{
|
||||
foreach (ScriptingObject scriptingObjectCriteria in excludeCriteria)
|
||||
{
|
||||
IEnumerable<ScriptingObject> matches = MatchCriteria(scriptingObjectCriteria, matchedObjects);
|
||||
matchedObjects = matchedObjects.Except(matches);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply additional filters if included.
|
||||
matchedObjects = ExcludeSchemaAndOrType(excludeSchemas, excludeTypes, matchedObjects);
|
||||
matchedObjects = IncludeSchemaAndOrType(includeSchemas, includeTypes, matchedObjects);
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
|
||||
private static IEnumerable<ScriptingObject> ExcludeSchemaAndOrType(IEnumerable<string> excludeSchemas, IEnumerable<string> excludeTypes,
|
||||
IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
// Given a list of candidates, we remove any objects that match the excluded schema and/or type.
|
||||
IEnumerable<ScriptingObject> remainingObjects = candidates;
|
||||
IEnumerable<ScriptingObject> matches = null;
|
||||
|
||||
if (excludeSchemas != null && excludeSchemas.Any())
|
||||
{
|
||||
foreach (string exclude_schema in excludeSchemas)
|
||||
{
|
||||
matches = MatchCriteria(exclude_schema, (candidate) => { return candidate.Schema; }, candidates);
|
||||
remainingObjects = remainingObjects.Except(matches);
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeTypes != null && excludeTypes.Any())
|
||||
{
|
||||
foreach (string exclude_type in excludeTypes)
|
||||
{
|
||||
matches = remainingObjects.Where(o => string.Equals(exclude_type, o.Type, StringComparison.OrdinalIgnoreCase));
|
||||
remainingObjects = remainingObjects.Except(matches);
|
||||
}
|
||||
}
|
||||
|
||||
return remainingObjects;
|
||||
}
|
||||
|
||||
private static IEnumerable<ScriptingObject> IncludeSchemaAndOrType(IEnumerable<string> includeSchemas, IEnumerable<string> includeTypes,
|
||||
IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
// Given a list of candidates, we return a new list of scripting objects that match
|
||||
// the schema and/or type filter.
|
||||
IEnumerable<ScriptingObject> matchedSchema = new List<ScriptingObject>();
|
||||
IEnumerable<ScriptingObject> matchedType = new List<ScriptingObject>();
|
||||
IEnumerable<ScriptingObject> matchedObjects = new List<ScriptingObject>();
|
||||
IEnumerable<ScriptingObject> matches = null;
|
||||
|
||||
if (includeSchemas != null && includeSchemas.Any())
|
||||
{
|
||||
foreach (string include_schema in includeSchemas)
|
||||
{
|
||||
matches = MatchCriteria(include_schema, (candidate) => { return candidate.Schema; }, candidates);
|
||||
matchedSchema = matchedSchema.Union(matches);
|
||||
}
|
||||
matchedObjects = matchedSchema;
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedObjects = candidates;
|
||||
}
|
||||
|
||||
if (includeTypes != null && includeTypes.Any())
|
||||
{
|
||||
foreach (string include_type in includeTypes)
|
||||
{
|
||||
matches = matchedObjects.Where(o => string.Equals(include_type, o.Type, StringComparison.OrdinalIgnoreCase));
|
||||
matchedType = matchedType.Union(matches);
|
||||
}
|
||||
matchedObjects = matchedType;
|
||||
}
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
|
||||
private static IEnumerable<ScriptingObject> MatchCriteria(ScriptingObject criteria, IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
Validate.IsNotNull("criteria", criteria);
|
||||
Validate.IsNotNull("candidates", candidates);
|
||||
|
||||
IEnumerable<ScriptingObject> matchedObjects = candidates;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(criteria.Type))
|
||||
{
|
||||
matchedObjects = matchedObjects.Where(o => string.Equals(criteria.Type, o.Type, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
matchedObjects = MatchCriteria(criteria.Schema, (candidate) => { return candidate.Schema; }, matchedObjects);
|
||||
matchedObjects = MatchCriteria(criteria.Name, (candidate) => { return candidate.Name; }, matchedObjects);
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
|
||||
private static IEnumerable<ScriptingObject> MatchCriteria(string property, Func<ScriptingObject, string> propertySelector, IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
IEnumerable<ScriptingObject> matchedObjects = candidates;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(property))
|
||||
{
|
||||
if (property.Equals(Wildcard, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Don't filter any objects
|
||||
}
|
||||
if (property.EndsWith(Wildcard, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
matchedObjects = candidates.Where(
|
||||
o =>
|
||||
propertySelector(o) != null &&
|
||||
propertySelector(o).StartsWith(
|
||||
propertySelector(o).Substring(0, propertySelector(o).Length - 1),
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedObjects = matchedObjects.Where(o => string.Equals(property, propertySelector(o), StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for scripting operations. Because scripting operations can be very long
|
||||
/// running, there my be multiple concurrent scripting operations. To distinguish events
|
||||
/// between concurrent scripting operations, use the operation id.
|
||||
/// </summary>
|
||||
public abstract class ScriptingOperation : IDisposable
|
||||
{
|
||||
private CancellationTokenSource cancellation = new CancellationTokenSource();
|
||||
|
||||
protected ScriptingOperation()
|
||||
{
|
||||
this.OperationId = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
protected CancellationToken CancellationToken { get { return this.cancellation.Token; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id associated with this instance.
|
||||
/// </summary>
|
||||
public string OperationId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Excecutes the scripting operation.
|
||||
/// </summary>
|
||||
public abstract void Execute();
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the scripting operation.
|
||||
/// </summary>
|
||||
public virtual void Cancel()
|
||||
{
|
||||
if (!this.cancellation.IsCancellationRequested)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, string.Format("Cancel invoked for OperationId {0}", this.OperationId));
|
||||
this.cancellation.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the scripting operation.
|
||||
/// </summary>
|
||||
public abstract void Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
//
|
||||
// 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.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.SqlScriptPublish;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to represent an in-progress script operation.
|
||||
/// </summary>
|
||||
public sealed class ScriptingScriptOperation : SmoScriptingOperation
|
||||
{
|
||||
|
||||
private int scriptedObjectCount = 0;
|
||||
|
||||
private int totalScriptedObjectCount = 0;
|
||||
|
||||
private int eventSequenceNumber = 1;
|
||||
|
||||
private string azureAccessToken;
|
||||
|
||||
public ScriptingScriptOperation(ScriptingParams parameters, string azureAccessToken): base(parameters)
|
||||
{
|
||||
this.azureAccessToken = azureAccessToken;
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
SqlScriptPublishModel publishModel = null;
|
||||
|
||||
try
|
||||
{
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
this.ValidateScriptDatabaseParams();
|
||||
|
||||
publishModel = BuildPublishModel();
|
||||
publishModel.ScriptItemsCollected += this.OnPublishModelScriptItemsCollected;
|
||||
publishModel.ScriptProgress += this.OnPublishModelScriptProgress;
|
||||
publishModel.ScriptError += this.OnPublishModelScriptError;
|
||||
publishModel.AllowSystemObjects = true;
|
||||
|
||||
ScriptDestination destination = !string.IsNullOrWhiteSpace(this.Parameters.ScriptDestination)
|
||||
? (ScriptDestination)Enum.Parse(typeof(ScriptDestination), this.Parameters.ScriptDestination)
|
||||
: ScriptDestination.ToSingleFile;
|
||||
|
||||
// SMO is currently hardcoded to produce UTF-8 encoding when running on dotnet core.
|
||||
ScriptOutputOptions outputOptions = new ScriptOutputOptions
|
||||
{
|
||||
SaveFileMode = ScriptFileMode.Overwrite,
|
||||
SaveFileName = this.Parameters.FilePath,
|
||||
ScriptDestination = destination,
|
||||
};
|
||||
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
publishModel.GenerateScript(outputOptions);
|
||||
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending script complete notification event for operation {0}, sequence number {1} with total count {2} and scripted count {3}",
|
||||
this.OperationId,
|
||||
this.eventSequenceNumber,
|
||||
this.totalScriptedObjectCount,
|
||||
this.scriptedObjectCount));
|
||||
|
||||
ScriptText = publishModel.RawScript;
|
||||
|
||||
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
|
||||
{
|
||||
Success = true,
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e.IsOperationCanceledException())
|
||||
{
|
||||
Logger.Write(TraceEventType.Information, string.Format("Scripting operation {0} was canceled", this.OperationId));
|
||||
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
|
||||
{
|
||||
Canceled = true,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Write(TraceEventType.Error, string.Format("Scripting operation {0} failed with exception {1}", this.OperationId, e));
|
||||
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
|
||||
{
|
||||
HasError = true,
|
||||
ErrorMessage = e.Message,
|
||||
ErrorDetails = e.ToString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (publishModel != null)
|
||||
{
|
||||
publishModel.ScriptItemsCollected -= this.OnPublishModelScriptItemsCollected;
|
||||
publishModel.ScriptProgress -= this.OnPublishModelScriptProgress;
|
||||
publishModel.ScriptError -= this.OnPublishModelScriptError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SendCompletionNotificationEvent(ScriptingCompleteParams parameters)
|
||||
{
|
||||
base.SendCompletionNotificationEvent(parameters);
|
||||
}
|
||||
|
||||
protected override void SendPlanNotificationEvent(ScriptingPlanNotificationParams parameters)
|
||||
{
|
||||
base.SendPlanNotificationEvent(parameters);
|
||||
}
|
||||
|
||||
protected override void SendProgressNotificationEvent(ScriptingProgressNotificationParams parameters)
|
||||
{
|
||||
base.SendProgressNotificationEvent(parameters);
|
||||
}
|
||||
|
||||
protected override void SetCommonEventProperties(ScriptingEventParams parameters)
|
||||
{
|
||||
base.SetCommonEventProperties(parameters);
|
||||
parameters.SequenceNumber = this.eventSequenceNumber;
|
||||
this.eventSequenceNumber += 1;
|
||||
}
|
||||
|
||||
private SqlScriptPublishModel BuildPublishModel()
|
||||
{
|
||||
SqlScriptPublishModel publishModel = new SqlScriptPublishModel(this.Parameters.ConnectionString);
|
||||
|
||||
// See if any filtering criteria was specified. If not, we're scripting the entire database. Otherwise, the filtering
|
||||
// criteria should include the target objects to script.
|
||||
//
|
||||
bool hasObjectsSpecified = this.Parameters.ScriptingObjects != null && this.Parameters.ScriptingObjects.Any();
|
||||
bool hasCriteriaSpecified =
|
||||
(this.Parameters.IncludeObjectCriteria != null && this.Parameters.IncludeObjectCriteria.Any()) ||
|
||||
(this.Parameters.ExcludeObjectCriteria != null && this.Parameters.ExcludeObjectCriteria.Any()) ||
|
||||
(this.Parameters.IncludeSchemas != null && this.Parameters.IncludeSchemas.Any()) ||
|
||||
(this.Parameters.ExcludeSchemas != null && this.Parameters.ExcludeSchemas.Any()) ||
|
||||
(this.Parameters.IncludeTypes != null && this.Parameters.IncludeTypes.Any()) ||
|
||||
(this.Parameters.ExcludeTypes != null && this.Parameters.ExcludeTypes.Any());
|
||||
bool scriptAllObjects = !hasObjectsSpecified && !hasCriteriaSpecified;
|
||||
|
||||
// In the getter for SqlScriptPublishModel.AdvancedOptions, there is some strange logic which will
|
||||
// cause the SqlScriptPublishModel.AdvancedOptions to get reset and lose all values based the ordering
|
||||
// of when SqlScriptPublishModel.ScriptAllObjects is set.
|
||||
//
|
||||
publishModel.ScriptAllObjects = scriptAllObjects;
|
||||
if (scriptAllObjects)
|
||||
{
|
||||
// Due to the getter logic within publishModel.AdvancedOptions, we explicitly populate the options
|
||||
// after we determine what objects we are scripting.
|
||||
//
|
||||
PopulateAdvancedScriptOptions(this.Parameters.ScriptOptions, publishModel.AdvancedOptions);
|
||||
return publishModel;
|
||||
}
|
||||
|
||||
IEnumerable<ScriptingObject> selectedObjects = new List<ScriptingObject>();
|
||||
|
||||
if (hasCriteriaSpecified)
|
||||
{
|
||||
// This is an expensive remote call to load all objects from the database.
|
||||
//
|
||||
List<ScriptingObject> allObjects = publishModel.GetDatabaseObjects();
|
||||
selectedObjects = ScriptingObjectMatcher.Match(
|
||||
this.Parameters.IncludeObjectCriteria,
|
||||
this.Parameters.ExcludeObjectCriteria,
|
||||
this.Parameters.IncludeSchemas,
|
||||
this.Parameters.ExcludeSchemas,
|
||||
this.Parameters.IncludeTypes,
|
||||
this.Parameters.ExcludeTypes,
|
||||
allObjects);
|
||||
}
|
||||
|
||||
if (hasObjectsSpecified)
|
||||
{
|
||||
selectedObjects = selectedObjects.Union(this.Parameters.ScriptingObjects);
|
||||
}
|
||||
|
||||
// Populating advanced options after we select our objects in question, otherwise we lose all
|
||||
// advanced options. After this call to PopulateAdvancedScriptOptions, DO NOT reference the
|
||||
// publishModel.AdvancedOptions getter as it will reset the options in the model.
|
||||
//
|
||||
PopulateAdvancedScriptOptions(this.Parameters.ScriptOptions, publishModel.AdvancedOptions);
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Information,
|
||||
string.Format(
|
||||
"Scripting object count {0}, objects: {1}",
|
||||
selectedObjects.Count(),
|
||||
string.Join(", ", selectedObjects)));
|
||||
|
||||
string server = GetServerNameFromLiveInstance(this.Parameters.ConnectionString, this.azureAccessToken);
|
||||
string database = new SqlConnectionStringBuilder(this.Parameters.ConnectionString).InitialCatalog;
|
||||
|
||||
foreach (ScriptingObject scriptingObject in selectedObjects)
|
||||
{
|
||||
publishModel.SelectedObjects.Add(scriptingObject.ToUrn(server, database));
|
||||
}
|
||||
return publishModel;
|
||||
}
|
||||
|
||||
private void OnPublishModelScriptError(object sender, ScriptEventArgs e)
|
||||
{
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending scripting error progress event, Urn={0}, OperationId={1}, Sequence={2}, Completed={3}, Error={4}",
|
||||
e.Urn,
|
||||
this.OperationId,
|
||||
this.eventSequenceNumber,
|
||||
e.Completed,
|
||||
e?.Error?.ToString() ?? "null"));
|
||||
|
||||
// Keep scripting...it's a best effort operation.
|
||||
e.ContinueScripting = true;
|
||||
|
||||
this.SendProgressNotificationEvent(new ScriptingProgressNotificationParams
|
||||
{
|
||||
ScriptingObject = e.Urn?.ToScriptingObject(),
|
||||
Status = e.GetStatus(),
|
||||
CompletedCount = this.scriptedObjectCount,
|
||||
TotalCount = this.totalScriptedObjectCount,
|
||||
ErrorMessage = e?.Error?.Message,
|
||||
ErrorDetails = e?.Error?.ToString(),
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPublishModelScriptItemsCollected(object sender, ScriptItemsArgs e)
|
||||
{
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
List<ScriptingObject> scriptingObjects = e.Urns.Select(urn => urn.ToScriptingObject()).ToList();
|
||||
this.totalScriptedObjectCount = scriptingObjects.Count;
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending scripting plan notification event OperationId={0}, Sequence={1}, Count={2}, Objects: {3}",
|
||||
this.OperationId,
|
||||
this.eventSequenceNumber,
|
||||
this.totalScriptedObjectCount,
|
||||
string.Join(", ", e.Urns)));
|
||||
|
||||
this.SendPlanNotificationEvent(new ScriptingPlanNotificationParams
|
||||
{
|
||||
ScriptingObjects = scriptingObjects,
|
||||
Count = scriptingObjects.Count,
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPublishModelScriptProgress(object sender, ScriptEventArgs e)
|
||||
{
|
||||
this.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (e.Completed)
|
||||
{
|
||||
this.scriptedObjectCount += 1;
|
||||
}
|
||||
|
||||
Logger.Write(
|
||||
TraceEventType.Verbose,
|
||||
string.Format(
|
||||
"Sending progress event, Urn={0}, OperationId={1}, Sequence={2}, Status={3}, Error={4}",
|
||||
e.Urn,
|
||||
this.OperationId,
|
||||
this.eventSequenceNumber,
|
||||
e.GetStatus(),
|
||||
e?.Error?.ToString() ?? "null"));
|
||||
|
||||
this.SendProgressNotificationEvent(new ScriptingProgressNotificationParams
|
||||
{
|
||||
ScriptingObject = e.Urn.ToScriptingObject(),
|
||||
Status = e.GetStatus(),
|
||||
CompletedCount = this.scriptedObjectCount,
|
||||
TotalCount = this.totalScriptedObjectCount,
|
||||
ErrorMessage = e?.Error?.Message,
|
||||
ErrorDetails = e?.Error?.ToString(),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
254
src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingService.cs
Normal file
254
src/Microsoft.Kusto.ServiceLayer/Scripting/ScriptingService.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// 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.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.Connection;
|
||||
using Microsoft.Kusto.ServiceLayer.Hosting;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.Kusto.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Main class for Scripting Service functionality
|
||||
/// </summary>
|
||||
public sealed class ScriptingService : IDisposable
|
||||
{
|
||||
private const int ScriptingOperationTimeout = 60000;
|
||||
|
||||
private static readonly Lazy<ScriptingService> LazyInstance = new Lazy<ScriptingService>(() => new ScriptingService());
|
||||
|
||||
public static ScriptingService Instance => LazyInstance.Value;
|
||||
|
||||
private static ConnectionService connectionService = null;
|
||||
|
||||
private readonly Lazy<ConcurrentDictionary<string, ScriptingOperation>> operations =
|
||||
new Lazy<ConcurrentDictionary<string, ScriptingOperation>>(() => new ConcurrentDictionary<string, ScriptingOperation>());
|
||||
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Internal for testing purposes only
|
||||
/// </summary>
|
||||
internal static ConnectionService ConnectionServiceInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (connectionService == null)
|
||||
{
|
||||
connectionService = ConnectionService.Instance;
|
||||
}
|
||||
return connectionService;
|
||||
}
|
||||
set
|
||||
{
|
||||
connectionService = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection of active operations
|
||||
/// </summary>
|
||||
internal ConcurrentDictionary<string, ScriptingOperation> ActiveOperations => operations.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Scripting Service instance
|
||||
/// </summary>
|
||||
/// <param name="serviceHost"></param>
|
||||
/// <param name="context"></param>
|
||||
public void InitializeService(ServiceHost serviceHost)
|
||||
{
|
||||
serviceHost.SetRequestHandler(ScriptingRequest.Type, this.HandleScriptExecuteRequest);
|
||||
serviceHost.SetRequestHandler(ScriptingCancelRequest.Type, this.HandleScriptCancelRequest);
|
||||
serviceHost.SetRequestHandler(ScriptingListObjectsRequest.Type, this.HandleListObjectsRequest);
|
||||
|
||||
// Register handler for shutdown event
|
||||
serviceHost.RegisterShutdownTask((shutdownParams, requestContext) =>
|
||||
{
|
||||
this.Dispose();
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles request to execute start the list objects operation.
|
||||
/// </summary>
|
||||
private async Task HandleListObjectsRequest(ScriptingListObjectsParams parameters, RequestContext<ScriptingListObjectsResult> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
ScriptingListObjectsOperation operation = new ScriptingListObjectsOperation(parameters);
|
||||
operation.CompleteNotification += (sender, e) => requestContext.SendEvent(ScriptingListObjectsCompleteEvent.Type, e);
|
||||
|
||||
RunTask(requestContext, operation);
|
||||
|
||||
await requestContext.SendResult(new ScriptingListObjectsResult { OperationId = operation.OperationId });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await requestContext.SendError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles request to start the scripting operation
|
||||
/// </summary>
|
||||
public async Task HandleScriptExecuteRequest(ScriptingParams parameters, RequestContext<ScriptingResult> requestContext)
|
||||
{
|
||||
SmoScriptingOperation operation = null;
|
||||
|
||||
try
|
||||
{
|
||||
// if a connection string wasn't provided as a parameter then
|
||||
// use the owner uri property to lookup its associated ConnectionInfo
|
||||
// and then build a connection string out of that
|
||||
ConnectionInfo connInfo = null;
|
||||
string accessToken = null;
|
||||
if (parameters.ConnectionString == null)
|
||||
{
|
||||
ScriptingService.ConnectionServiceInstance.TryFindConnection(parameters.OwnerUri, out connInfo);
|
||||
if (connInfo != null)
|
||||
{
|
||||
parameters.ConnectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
|
||||
accessToken = connInfo.ConnectionDetails.AzureAccountToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Could not find ConnectionInfo");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldCreateScriptAsOperation(parameters))
|
||||
{
|
||||
operation = new ScriptingScriptOperation(parameters, accessToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
operation = new ScriptAsScriptingOperation(parameters, accessToken);
|
||||
}
|
||||
|
||||
operation.PlanNotification += (sender, e) => requestContext.SendEvent(ScriptingPlanNotificationEvent.Type, e).Wait();
|
||||
operation.ProgressNotification += (sender, e) => requestContext.SendEvent(ScriptingProgressNotificationEvent.Type, e).Wait();
|
||||
operation.CompleteNotification += (sender, e) => this.SendScriptingCompleteEvent(requestContext, ScriptingCompleteEvent.Type, e, operation, parameters.ScriptDestination);
|
||||
|
||||
RunTask(requestContext, operation);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await requestContext.SendError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldCreateScriptAsOperation(ScriptingParams parameters)
|
||||
{
|
||||
// Scripting as operation should be used to script one object.
|
||||
// Scripting data and scripting to file is not supported by scripting as operation
|
||||
// To script Select, alter and execute use scripting as operation. The other operation doesn't support those types
|
||||
if( (parameters.ScriptingObjects != null && parameters.ScriptingObjects.Count == 1 && parameters.ScriptOptions != null
|
||||
&& parameters.ScriptOptions.TypeOfDataToScript == "SchemaOnly" && parameters.ScriptDestination == "ToEditor") ||
|
||||
parameters.Operation == ScriptingOperationType.Select || parameters.Operation == ScriptingOperationType.Execute ||
|
||||
parameters.Operation == ScriptingOperationType.Alter)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles request to cancel a script operation.
|
||||
/// </summary>
|
||||
public async Task HandleScriptCancelRequest(ScriptingCancelParams parameters, RequestContext<ScriptingCancelResult> requestContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
ScriptingOperation operation = null;
|
||||
if (this.ActiveOperations.TryRemove(parameters.OperationId, out operation))
|
||||
{
|
||||
operation.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Write(TraceEventType.Information, string.Format("Operation {0} was not found", operation.OperationId));
|
||||
}
|
||||
|
||||
await requestContext.SendResult(new ScriptingCancelResult());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await requestContext.SendError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async void SendScriptingCompleteEvent<TParams>(RequestContext<ScriptingResult> requestContext, EventType<TParams> eventType, TParams parameters,
|
||||
SmoScriptingOperation operation, string scriptDestination)
|
||||
{
|
||||
await requestContext.SendEvent(eventType, parameters);
|
||||
switch (scriptDestination)
|
||||
{
|
||||
case "ToEditor":
|
||||
await requestContext.SendResult(new ScriptingResult { OperationId = operation.OperationId, Script = operation.ScriptText });
|
||||
break;
|
||||
case "ToSingleFile":
|
||||
await requestContext.SendResult(new ScriptingResult { OperationId = operation.OperationId });
|
||||
break;
|
||||
default:
|
||||
await requestContext.SendError(string.Format("Operation {0} failed", operation.ToString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the async task that performs the scripting operation.
|
||||
/// </summary>
|
||||
private void RunTask<T>(RequestContext<T> context, ScriptingOperation operation)
|
||||
{
|
||||
ScriptingTask = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
this.ActiveOperations[operation.OperationId] = operation;
|
||||
operation.Execute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await context.SendError(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ScriptingOperation temp;
|
||||
this.ActiveOperations.TryRemove(operation.OperationId, out temp);
|
||||
}
|
||||
}).ContinueWithOnFaulted(async t => await context.SendError(t.Exception));
|
||||
}
|
||||
|
||||
internal Task ScriptingTask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the scripting service and all active scripting operations.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
disposed = true;
|
||||
|
||||
foreach (ScriptingScriptOperation operation in this.ActiveOperations.Values)
|
||||
{
|
||||
operation.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// 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 Microsoft.Kusto.ServiceLayer.Connection;
|
||||
using Microsoft.Kusto.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.Kusto.ServiceLayer.DataSource;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using static Microsoft.SqlServer.Management.SqlScriptPublish.SqlScriptOptions;
|
||||
|
||||
namespace Microsoft.Kusto.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all SMO scripting operations
|
||||
/// </summary>
|
||||
public abstract class SmoScriptingOperation : ScriptingOperation
|
||||
{
|
||||
private bool disposed = false;
|
||||
|
||||
public SmoScriptingOperation(ScriptingParams parameters)
|
||||
{
|
||||
Validate.IsNotNull("parameters", parameters);
|
||||
|
||||
this.Parameters = parameters;
|
||||
}
|
||||
|
||||
protected ScriptingParams Parameters { get; set; }
|
||||
|
||||
public string ScriptText { get; protected set; }
|
||||
|
||||
/// <remarks>
|
||||
/// An event can be completed by the following conditions: success, cancel, error.
|
||||
/// </remarks>
|
||||
public event EventHandler<ScriptingCompleteParams> CompleteNotification;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a scripting operation has made forward progress.
|
||||
/// </summary>
|
||||
public event EventHandler<ScriptingProgressNotificationParams> ProgressNotification;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a scripting operation has resolved which database objects will be scripted.
|
||||
/// </summary>
|
||||
public event EventHandler<ScriptingPlanNotificationParams> PlanNotification;
|
||||
|
||||
protected virtual void SendCompletionNotificationEvent(ScriptingCompleteParams parameters)
|
||||
{
|
||||
this.SetCommonEventProperties(parameters);
|
||||
this.CompleteNotification?.Invoke(this, parameters);
|
||||
}
|
||||
|
||||
protected virtual void SendProgressNotificationEvent(ScriptingProgressNotificationParams parameters)
|
||||
{
|
||||
this.SetCommonEventProperties(parameters);
|
||||
this.ProgressNotification?.Invoke(this, parameters);
|
||||
}
|
||||
|
||||
protected virtual void SendPlanNotificationEvent(ScriptingPlanNotificationParams parameters)
|
||||
{
|
||||
this.SetCommonEventProperties(parameters);
|
||||
this.PlanNotification?.Invoke(this, parameters);
|
||||
}
|
||||
|
||||
protected virtual void SetCommonEventProperties(ScriptingEventParams parameters)
|
||||
{
|
||||
parameters.OperationId = this.OperationId;
|
||||
}
|
||||
|
||||
protected string GetServerNameFromLiveInstance(string connectionString, string azureAccessToken)
|
||||
{
|
||||
string serverName = string.Empty;
|
||||
|
||||
using(var dataSource = DataSourceFactory.Create(DataSourceType.Kusto, connectionString, azureAccessToken))
|
||||
{
|
||||
serverName = dataSource.ClusterName;
|
||||
}
|
||||
|
||||
Logger.Write(TraceEventType.Verbose, string.Format("Resolved server name '{0}'", serverName));
|
||||
return serverName;
|
||||
}
|
||||
|
||||
protected void ValidateScriptDatabaseParams()
|
||||
{
|
||||
try
|
||||
{
|
||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(this.Parameters.ConnectionString);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ArgumentException(SR.ScriptingParams_ConnectionString_Property_Invalid, e);
|
||||
}
|
||||
if (this.Parameters.FilePath == null && this.Parameters.ScriptDestination != "ToEditor")
|
||||
{
|
||||
throw new ArgumentException(SR.ScriptingParams_FilePath_Property_Invalid);
|
||||
}
|
||||
else if (this.Parameters.FilePath != null && this.Parameters.ScriptDestination != "ToEditor")
|
||||
{
|
||||
if (!Directory.Exists(Path.GetDirectoryName(this.Parameters.FilePath)))
|
||||
{
|
||||
throw new ArgumentException(SR.ScriptingParams_FilePath_Property_Invalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void PopulateAdvancedScriptOptions(ScriptOptions scriptOptionsParameters, object advancedOptions)
|
||||
{
|
||||
if (scriptOptionsParameters == null)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, "No advanced options set, the ScriptOptions object is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (PropertyInfo optionPropInfo in scriptOptionsParameters.GetType().GetProperties())
|
||||
{
|
||||
PropertyInfo advancedOptionPropInfo = advancedOptions.GetType().GetProperty(optionPropInfo.Name);
|
||||
if (advancedOptionPropInfo == null)
|
||||
{
|
||||
Logger.Write(TraceEventType.Warning, string.Format("Invalid property info name {0} could not be mapped to a property on SqlScriptOptions.", optionPropInfo.Name));
|
||||
continue;
|
||||
}
|
||||
|
||||
object optionValue = optionPropInfo.GetValue(scriptOptionsParameters, index: null);
|
||||
if (optionValue == null)
|
||||
{
|
||||
Logger.Write(TraceEventType.Verbose, string.Format("Skipping ScriptOptions.{0} since value is null", optionPropInfo.Name));
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// The ScriptOptions property types from the request will be either a string or a bool?.
|
||||
// The SqlScriptOptions property types from SMO will all be an Enum. Using reflection, we
|
||||
// map the request ScriptOptions values to the SMO SqlScriptOptions values.
|
||||
//
|
||||
|
||||
try
|
||||
{
|
||||
object smoValue = null;
|
||||
if (optionPropInfo.PropertyType == typeof(bool?))
|
||||
{
|
||||
if (advancedOptionPropInfo.PropertyType == typeof(bool))
|
||||
{
|
||||
|
||||
smoValue = (bool)optionValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
smoValue = (bool)optionValue ? BooleanTypeOptions.True : BooleanTypeOptions.False;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smoValue = Enum.Parse(advancedOptionPropInfo.PropertyType, (string)optionValue, ignoreCase: true);
|
||||
}
|
||||
|
||||
Logger.Write(TraceEventType.Verbose, string.Format("Setting ScriptOptions.{0} to value {1}", optionPropInfo.Name, smoValue));
|
||||
advancedOptionPropInfo.SetValue(advancedOptions, smoValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Write(
|
||||
TraceEventType.Warning,
|
||||
string.Format("An exception occurred setting option {0} to value {1}: {2}", optionPropInfo.Name, optionValue, e));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the scripting operation.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
this.Cancel();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user