Agent configuration support classes (WIP) (#632)

* Additional SQL Agent config classes (WIP)

* Fix build breaks

* Clean up job step code

* Add VS Code build files

* Move changes to other machine

* More of the action execution classes

* More execution processing refactors

* More refactoring

* Disable tests for WIP merge

* Fix break on Release config

* Stage changes to other machine.
This commit is contained in:
Karl Burtram
2018-06-07 12:08:24 -07:00
committed by GitHub
parent 35b19320d4
commit 372ca0cbe8
73 changed files with 12650 additions and 374 deletions

View File

@@ -0,0 +1,14 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.Management
{
internal enum ConfigAction
{
Create,
Update,
Drop
}
}

View File

@@ -0,0 +1,344 @@
using System;
using System.Collections;
using System.Text;
using Microsoft.SqlServer.Management.Diagnostics;
using Microsoft.SqlTools.ServiceLayer.Agent;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// defines mandatory interface that an action must implement
/// </summary>
public interface IManagementAction
{
ExecutionMode LastExecutionResult { get; }
/// <summary>
/// performs custom action when user requests to cancel execution.
/// </summary>
/// <param name="sender"></param>
void OnCancel(object sender);
/// <summary>
/// Overridable function that allow a derived class to implement its
/// OnScript functionality.
/// </summary>
/// <param name="sender"></param>
/// <returns>text of the generated script</returns>
string OnScript(object sender);
/// <summary>
/// Overridable function that allow a derived class to implement its
/// OnRunNow functionality
/// </summary>
/// <param name="sender"></param>
void OnRunNow(object sender);
/// <summary>
/// Overridable function that allow a derived class to implement
/// a finalizing action after a RunNow or RunNowAndClose were sucesfully executed
/// NOTE: same as OnGatherUiInformation, this method is always called from UI thread
/// </summary>
/// <param name="sender"></param>
void OnTaskCompleted(object sender, ExecutionMode executionMode, RunType executionType);
}
/// <summary>
/// This class is responsible for executing panels one by one.
/// It is reused by ViewSwitcherControlsManager and treepanelform classes
/// </summary>
internal class ExecutionHandlerDelegate
{
private object cancelCriticalSection = new object();
private IManagementAction managementAction;
public ExecutionHandlerDelegate(IManagementAction managementAction)
{
this.managementAction = managementAction;
}
/// <summary>
/// </summary>
/// <param name="runType"></param>
/// <param name="sender"></param>
/// <returns>execution result</returns>
public ExecutionMode Run(RunType runType, object sender)
{
//dispatch the call to the right method
switch (runType)
{
case RunType.RunNow:
this.managementAction.OnRunNow(sender);
break;
case RunType.ScriptToWindow:
this.managementAction.OnScript(sender);
break;
default:
throw new InvalidOperationException("SRError.UnexpectedRunType");
}
if((this.managementAction.LastExecutionResult == ExecutionMode.Failure) ||
(this.managementAction.LastExecutionResult == ExecutionMode.Cancel))
{
return this.managementAction.LastExecutionResult;
}
// if we're here, everything went fine
return ExecutionMode.Success;
}
/// <summary>
/// performs custom action wen user requests a cancel
/// this is called from the UI thread
/// </summary>
/// <param name="sender"></param>
public void Cancel(object sender)
{
lock (this.cancelCriticalSection)
{
this.managementAction.OnCancel(sender);
}
}
}
/// <summary>
/// manager that hooks up tree view with the individual views
/// </summary>
internal sealed class ExecutonHandler : IDisposable
{
/// <summary>
/// handler that we delegate execution related tasks to
/// </summary>
private ExecutionHandlerDelegate panelExecutionHandler;
/// <summary>
/// class that describes available views
/// </summary>
private ISqlControlCollection viewsHolder;
/// <summary>
/// class that describes available views that is also aware of execution
/// </summary>
private IExecutionAwareManagementAction managementAction;
/// <summary>
/// result of the last execution
/// </summary>
private ExecutionMode executionResult;
/// <summary>
/// text of the generated script if RunNow method was called last time with scripting option
/// </summary>
private StringBuilder script;
/// <summary>
/// index of the panel that is being executed
/// </summary>
private int currentlyExecutingPanelIndex;
/// <summary>
/// creates instance of the class and returns service provider that aggregates the provider
/// provider with extra services
/// </summary>
/// <param name="serviceProvider">service provider from the host</param>
/// <param name="aggregatedProvider">
/// aggregates service provider that is derived from the host service provider and
/// is extended with extra services and/or overriden services. The host should be
/// using this provider whenever it has to specify an IServiceProvider to a component
/// that will be managed by this class
/// </param>
public ExecutonHandler(IExecutionAwareManagementAction managementAction)
{
this.managementAction = managementAction;
this.panelExecutionHandler = new ExecutionHandlerDelegate(managementAction);
}
#region public interface
public ExecutionMode ExecutionResult
{
get
{
return this.executionResult;
}
}
/// <summary>
/// text of the generated script if RunNow method was called last time with scripting option
/// </summary>
public string ScriptTextFromLastRun
{
get
{
if (this.script != null)
{
return this.script.ToString();
}
else
{
return string.Empty;
}
}
}
/// <summary>
/// we call the run now implementaion of the management action.
/// If any exception is generated we stop the execution and we set the execution mode flag to failure.
/// </summary>
/// <param name="sender"></param>
public void RunNow(RunType runType, object sender)
{
try
{
// reset some internal vars
this.executionResult = ExecutionMode.Failure;
this.currentlyExecutingPanelIndex = -1; // will become 0 if we're executing on view by view basis
// ensure that we have valid StringBulder for scripting
if (IsScripting(runType))
{
EnsureValidScriptBuilder();
}
// do preprocess action. It is possible to do entire execution from inside this method
if (this.managementAction != null)
{
PreProcessExecutionInfo preProcessInfo = new PreProcessExecutionInfo(runType);
if (!this.managementAction.PreProcessExecution(preProcessInfo, out this.executionResult))
{
// In case of scripting preProcessInfo.Script must contain text of the script
if (executionResult == ExecutionMode.Success && IsScripting(runType) && preProcessInfo.Script != null)
{
this.script.Append(preProcessInfo.Script);
}
return; // result of execution is in executionResult
}
}
// NOTE: post process action is done in finally block below
// start executing
this.executionResult = this.panelExecutionHandler.Run(runType, sender);
}
#region error handling
catch (OutOfMemoryException)
{
throw;
}
catch (System.Threading.ThreadAbortException)
{
throw;
}
catch (OperationCanceledException)
{
this.executionResult = ExecutionMode.Cancel;
}
catch (Exception e)
{
ProcessExceptionDuringExecution(e, this.currentlyExecutingPanelIndex);
return;
}
finally
{
//do postprocess action
if (this.managementAction != null)
{
this.managementAction.PostProcessExecution(runType, this.executionResult);
}
}
#endregion
}
/// <summary>
/// Kicks off Cancel operation
/// </summary>
/// <param name="sender"></param>
public void InitiateCancel(object sender)
{
if (this.managementAction != null)
{
if (!this.managementAction.Cancel())
{
//everything was done inside this method
this.executionResult = ExecutionMode.Cancel;
return;
}
}
//otherwise do cancel ourselves
// if everything goes OK, Run() method will return with Cancel result
this.panelExecutionHandler.Cancel(sender);
}
/// <summary>
/// is called by the host to do post execution actions
/// </summary>
/// <param name="sender"></param>
/// <param name="executionMode"></param>
/// <param name="executionType"></param>
public void OnTaskCompleted(object sender, ExecutionMode executionResult, RunType executionType)
{
}
/// <summary>
/// enables deterministic cleanup
/// </summary>
public void Dispose()
{
IDisposable managementActionAsDisposable = this.managementAction as IDisposable;
if (managementActionAsDisposable != null)
{
managementActionAsDisposable.Dispose();
}
}
#endregion
#region private helpers
/// <summary>
/// determines whether given run type corresponds to scripting or not
/// </summary>
/// <param name="runType"></param>
/// <returns></returns>
private bool IsScripting(RunType runType)
{
return (runType == RunType.ScriptToClipboard ||
runType == RunType.ScriptToFile ||
runType == RunType.ScriptToWindow ||
runType == RunType.ScriptToJob);
}
/// <summary>
/// ensure that we have valid StringBulder for scripting
/// </summary>
private void EnsureValidScriptBuilder()
{
if (this.script == null)
{
this.script = new StringBuilder(256);
}
else
{
this.script.Length = 0;
}
}
/// <summary>
/// helper function that is called when we caught an exception during execution
/// </summary>
/// <param name="e"></param>
/// <param name="failedViewIndex">-1 indicates that we don't know</param>
private void ProcessExceptionDuringExecution(Exception e, int failedViewIndex)
{
//show the error
this.executionResult = ExecutionMode.Failure;
}
#endregion
}
}

View File

@@ -0,0 +1,32 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// Execution mode enumeration Success if execution succeeded of Failure otherwise for now.
/// This enumeration might be refined more as there are needs for it
/// </summary>
public enum ExecutionMode
{
/// <summary>
/// indicates that the operation failed
/// </summary>
Failure = 0,
/// <summary>
/// indicates that the operation succeded
/// </summary>
Success,
/// <summary>
/// indicates that the operation was canceled
/// </summary>
Cancel
};
}

View File

@@ -0,0 +1,86 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
public class PreProcessExecutionInfo
{
private RunType runType;
private string script;
private PreProcessExecutionInfo() {}
internal PreProcessExecutionInfo(RunType runType)
{
this.runType = runType;
}
public RunType RunType
{
get
{
return this.runType;
}
}
public string Script
{
get
{
return this.script;
}
set
{
this.script = value;
}
}
}
/// <summary>
/// IExecutionAwareSqlControlCollection allows control's container to do pre and post
/// processing of the execution commands
/// </summary>
public interface IExecutionAwareManagementAction : ISqlControlCollection, IManagementAction
{
/// <summary>
/// called before dialog's host executes actions on all panels in the dialog one by one.
/// If something fails inside this function and the execution should be aborted,
/// it can either raise an exception [in which case the framework will show message box with exception text]
/// or set executionResult out parameter to be ExecutionMode.Failure
/// NOTE: it might be called from worker thread
/// </summary>
/// <param name="executionInfo">information about execution action</param>
/// <param name="executionResult">result of the execution</param>
/// <returns>
/// true if regular execution should take place, false if everything
/// has been done by this function
/// NOTE: in case of returning false during scripting operation
/// PreProcessExecutionInfo.Script property of executionInfo parameter
/// MUST be set by this function [if execution result is success]
/// </returns>
bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult);
/// <summary>
/// called when the host received Cancel request. NOTE: this method can return while
/// operation is still being canceled
/// </summary>
/// <returns>
/// true if the host should do standard cancel for the currently running view or
/// false if the Cancel operation was done entirely inside this method and there is nothing
/// extra that should be done
/// </returns>
bool Cancel();
/// <summary>
/// called after host executes actions
/// </summary>
/// <param name="executionMode">result of the execution</param>
/// <param name="runType">type of execution</param>
void PostProcessExecution(RunType runType, ExecutionMode executionMode);
}
}

View File

@@ -0,0 +1,26 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
#region interfaces
/// <summary>
/// Interface that supports the delegation of individual actions in the progress dialog
/// to individual classes.
/// </summary>
public interface IProgressItem
{
/// <summary>
/// Perform the action for this class
/// </summary>
/// <param name="actions">Actions collection</param>
/// <param name="index">array index of this particular action</param>
/// <returns></returns>
ProgressStatus DoAction(ProgressItemCollection actions, int index);
}
#endregion
}

View File

@@ -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.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// defines notion of sitable object
/// </summary>
public interface IObjectWithSite
{
void SetSite(System.IServiceProvider sp);
}
/// <summary>
/// ISqlControlCollection allows access to a collection of dialog views
/// </summary>
public interface ISqlControlCollection : IObjectWithSite
{
}
}

View File

@@ -0,0 +1,784 @@
//
// 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;
using System.Collections.Specialized;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Text;
using System.Xml;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Diagnostics;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Admin;
using Microsoft.SqlTools.ServiceLayer.Agent;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// base class that can be used to derived from for the main classes
/// </summary>
public class ManagementActionBase : IDisposable, IExecutionAwareManagementAction
{
#region Members
/// <summary>
/// service provider of our host. We should direct all host-specific requests to the services
/// implemented by this provider
/// </summary>
private IServiceProvider serviceProvider;
/// <summary>
/// data container with initialization-related information
/// </summary>
private CDataContainer dataContainer;
//whether we assume complete ownership over it.
//We set this member once the dataContainer is set to be non-null
private bool ownDataContainer = true;
//if derived class tries to call a protected method that relies on service provider,
//and the service provider hasn't been set yet, we will cache the values and will
//propagate them when we get the provider set
//private System.Drawing.Icon cachedIcon = null;
private string cachedCaption = null;
//SMO Server connection that MUST be used for all enumerator calls
//We'll get this object out of CDataContainer, that must be initialized
//property by the initialization code
private ServerConnection serverConnection;
private ExecutionHandlerDelegate cachedPanelExecutionHandler;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
public ManagementActionBase()
{
}
#endregion
#region IDisposable implementation
void IDisposable.Dispose()
{
//BUGBUG - do we need finalizer
Dispose(true);//call protected virtual method
}
/// <summary>
/// do the deterministic cleanup
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
//dispose CDataContainer if needed
if (this.dataContainer != null)
{
if (this.ownDataContainer)
{
this.dataContainer.Dispose();
}
this.dataContainer = null;
}
}
#endregion
#region IObjectWithSite implementation
public virtual void SetSite(IServiceProvider sp)
{
if (sp == null)
{
throw new ArgumentNullException("sp");
}
//allow to be sited only once
if (this.serviceProvider == null)
{
//cache the service provider
this.serviceProvider = sp;
//call protected virtual method to enable derived classes to do initialization
//OnHosted();
}
}
#endregion
#region IExecutionAwareSqlControlCollection implementation
/// <summary>
/// called before dialog's host executes actions on all panels in the dialog one by one.
/// If something fails inside this function and the execution should be aborted,
/// it can either raise an exception [in which case the framework will show message box with exception text]
/// or set executionResult out parameter to be ExecutionMode.Failure
/// NOTE: it might be called from worker thread
/// </summary>
/// <param name="executionInfo">information about execution action</param>
/// <param name="executionResult">result of the execution</param>
/// <returns>
/// true if regular execution should take place, false if everything
/// has been done by this function
/// NOTE: in case of returning false during scripting operation
/// PreProcessExecutionInfo.Script property of executionInfo parameter
/// MUST be set by this function [if execution result is success]
/// </returns>
public bool PreProcessExecution(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
{
// we start from failure
executionResult = ExecutionMode.Failure;
// OK, we do server switching for scripting for SQL Server execution here
RunType runType = executionInfo.RunType;
if (IsScripting(runType))
{
if (!PreProcessScripting(executionInfo, out executionResult))
{
return false;
}
}
if (DataContainer != null)
{
// we take over execution here. We substitute the server here for SQL containers
if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
{
ExecuteForSql(executionInfo, out executionResult);
return false; // execution of the entire action was done here
}
}
// call virtual function to do regular execution
return DoPreProcessExecution(executionInfo.RunType, out executionResult);
}
/// <summary>
/// called after dialog's host executes actions on all panels in the dialog one by one
/// NOTE: it might be called from worker thread
/// </summary>
/// <param name="executionMode">result of the execution</param>
/// <param name="runType">type of execution</param>
public void PostProcessExecution(RunType runType, ExecutionMode executionResult)
{
//delegate to the protected virtual method
DoPostProcessExecution(runType, executionResult);
}
/// <summary>
/// called when the host received Cancel request. NOTE: this method can return while
/// operation is still being canceled
/// </summary>
/// <returns>
/// true if the host should do standard cancel for the currently running view or
/// false if the Cancel operation was done entirely inside this method and there is nothing
/// extra that should be done
/// </returns>
public bool Cancel()
{
return true;
}
#endregion
/// <summary>
/// whether we own our DataContainer or not. Depending on this value it will or won't be
/// disposed in our Dispose method
/// </summary>
protected virtual bool OwnDataContainer
{
get
{
//by default we own it
return true;
}
}
/// <summary>
/// called by IExecutionAwareSqlControlCollection.PreProcessExecution to enable derived
/// classes to take over execution of the dialog and do entire execution in this method
/// rather than having the framework to execute dialog views one by one.
///
/// NOTE: it might be called from non-UI thread
/// </summary>
/// <param name="runType"></param>
/// <param name="executionResult"></param>
/// <returns>
/// true if regular execution should take place, false if everything,
/// has been done by this function
/// </returns>
protected virtual bool DoPreProcessExecution(RunType runType, out ExecutionMode executionResult)
{
//ask the framework to do normal execution by calling OnRunNOw methods
//of the views one by one
executionResult = ExecutionMode.Success;
return true;
}
/// <summary>
/// called after dialog's host executes actions on all panels in the dialog one by one
/// NOTE: it might be called from worker thread
/// </summary>
/// <param name="executionMode">result of the execution</param>
/// <param name="runType">type of execution</param>
protected virtual void DoPostProcessExecution(RunType runType, ExecutionMode executionResult)
{
//nothing to do in the base class
}
/// <summary>
/// called before dialog's host executes OnReset method on all panels in the dialog one by one
/// NOTE: it might be called from worker thread
/// </summary>
/// <returns>
/// true if regular execution should take place, false if everything
/// has been done by this function
/// </returns>
/// <returns></returns>
protected virtual bool DoPreProcessReset()
{
if ((this.dataContainer != null) && this.dataContainer.IsNewObject)
{
this.dataContainer.Reset();
}
return true;
}
/// <summary>
/// called after dialog's host executes OnReset method on all panels in the dialog one by one
/// NOTE: it might be called from worker thread
/// </summary>
protected virtual void DoPostProcessReset()
{
//nothing in the base class
}
/// <summary>
/// Called to intercept scripting operation
/// </summary>
/// <param name="executionInfo"></param>
/// <param name="executionResult"></param>
/// <returns>
/// true if regular execution should take place, false the script
/// has been created by this function
/// </returns>
protected virtual bool PreProcessScripting(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
{
//we don't do anything here, but we enable derived classes to do something...
executionResult = ExecutionMode.Success;
return true;
}
/// <summary>
/// CDataContainer accessor
/// </summary>
protected CDataContainer DataContainer
{
get
{
return this.dataContainer;
}
set
{
this.dataContainer = value;
this.ownDataContainer = OwnDataContainer; //cache the value
}
}
/// <summary>
/// SMO Server connection that MUST be used for all enumerator calls
/// We'll get this object out of CDataContainer, that must be initialized
/// property by the initialization code
/// </summary>
protected ServerConnection ServerConnection
{
get
{
if (this.serverConnection == null && this.dataContainer != null &&
this.dataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
{
this.serverConnection = this.dataContainer.ServerConnection;
}
//it CAN be null here if dataContainer hasn't been set or the container type is non SQL
return this.serverConnection;
}
}
/// <summary>
/// checks whether given run time represents one of scripting options
/// </summary>
/// <param name="runType"></param>
/// <returns></returns>
protected static bool IsScripting(RunType runType)
{
return(runType != RunType.RunNow && runType != RunType.RunNowAndExit);
}
/// <summary>
/// calls DoPreProcessExecution and if it returns false, then it will execute all initialized views
/// one by one.
/// This method allows derived classes to do both preprocessing and normal execution in a way
/// that the framework would normally do. Use this method for special cases when derived class
/// should really handle entire execution while doing exactly the same actions as the framework
/// would do
/// </summary>
/// <param name="runType"></param>
/// <returns>
/// result of the execution. It will let exception fly out if it was raised during execution
/// </returns>
protected ExecutionMode DoPreProcessExecutionAndRunViews(RunType runType)
{
ExecutionMode executionResult;
if (DoPreProcessExecution(runType, out executionResult))
{
// true return value means that we need to do execution ourselves
executionResult = PanelExecutionHandler.Run(runType, this);
}
return executionResult;
}
/// <summary>
/// determines whether we need to substitute SMO server objects with the
/// temporary ones while doing scripting
/// </summary>
/// <returns></returns>
private bool NeedToSwitchServer
{
get
{
ServerSwitchingAttribute switchingAttrib =
Utils.GetCustomAttribute(this, typeof(ServerSwitchingAttribute)) as ServerSwitchingAttribute;
if (DataContainer == null)
{
if (switchingAttrib != null)
{
return switchingAttrib.NeedToSwtichServerObject;
}
else
{
throw new InvalidOperationException();
}
}
if (DataContainer.ContainerServerType == CDataContainer.ServerType.SQL)
{
if (switchingAttrib != null)
{
return switchingAttrib.NeedToSwtichServerObject;
}
else
{
return true;//switch by default in SQL case
}
}
else
{
throw new InvalidOperationException();
}
}
}
protected virtual ServerConnection GetServerConnectionForScript()
{
return this.dataContainer.Server.ConnectionContext;
}
/// <summary>
/// builds a script string from SMO string collections. This function should
/// probably be moved to SMO. Also we need a way to specify the tsql batch
/// separator which for now is GO
/// </summary>
/// <returns></returns>
private string BuildSqlScript()
{
SqlSmoObject sqlDialogSubject = null;
try
{
sqlDialogSubject = this.DataContainer.SqlDialogSubject;
}
catch (System.Exception)
{
// We may not have a valid dialog subject here (such as if the object hasn't been created yet)
// so in that case we'll just ignore it as that's a normal scenario.
}
StringCollection sc = GetServerConnectionForScript().CapturedSql.Text;
// Scripting may happen on either the server ExecutionManager or the
// ExecutionManager of the object itself. So we make sure to check
// the subject text if the server ExecutionManager didn't have any
// scripts after doing the scripting
if (sc.Count == 0 && sqlDialogSubject != null)
{
sc = sqlDialogSubject.ExecutionManager.ConnectionContext.CapturedSql.Text;
}
StringBuilder script = new StringBuilder(4096);
if (sc != null)
{
for (int i = 0; i < sc.Count; i ++)
{
script.AppendFormat("{0}\r\nGO\r\n", sc[i].ToString());
}
}
return script.ToString();
}
/// <summary>
/// called when we need to script a Sql Server action
/// </summary>
/// <param name="executionInfo"></param>
/// <param name="executionResult"></param>
private void ExecuteForSql(PreProcessExecutionInfo executionInfo, out ExecutionMode executionResult)
{
Microsoft.SqlServer.Management.Smo.Server oldServer = null;
if (NeedToSwitchServer)
{
// We use a new instance of the SMO Server object every time we script
// so that any changes that are made to the SMO server while scripting are
// not kept when the script operation is completed.
oldServer = DataContainer.Server;
//BUGBUG - see if we can use copy ctor instead
DataContainer.Server = new Microsoft.SqlServer.Management.Smo.Server(DataContainer.ServerConnection);
}
string szScript = null;
bool isScripting = IsScripting(executionInfo.RunType);
var executionModeOriginal = GetServerConnectionForScript().SqlExecutionModes;
// For Azure the ExecutionManager is different depending on which ExecutionManager
// used - one at the Server level and one at the Database level. So to ensure we
// don't use the wrong execution mode we need to set the mode for both (for on-prem
// this will essentially be a no-op)
SqlExecutionModes subjectExecutionModeOriginal = executionModeOriginal;
SqlSmoObject sqlDialogSubject = null;
try
{
sqlDialogSubject = this.DataContainer.SqlDialogSubject;
}
catch (System.Exception)
{
// We may not have a valid dialog subject here (such as if the object hasn't been created yet)
// so in that case we'll just ignore it as that's a normal scenario.
}
if (sqlDialogSubject != null)
{
subjectExecutionModeOriginal = sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes;
}
try
{
SqlExecutionModes newMode = isScripting
? SqlExecutionModes.CaptureSql
: SqlExecutionModes.ExecuteSql;
// now, do the execution itself
GetServerConnectionForScript().SqlExecutionModes = newMode;
if (sqlDialogSubject != null)
{
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = newMode;
}
executionResult = DoPreProcessExecutionAndRunViews(executionInfo.RunType);
if (isScripting)
{
if (executionResult == ExecutionMode.Success)
{
szScript = BuildSqlScript();
}
}
}
finally
{
GetServerConnectionForScript().SqlExecutionModes = executionModeOriginal;
if (isScripting)
{
GetServerConnectionForScript().CapturedSql.Clear();
}
if (sqlDialogSubject != null)
{
sqlDialogSubject.ExecutionManager.ConnectionContext.SqlExecutionModes = subjectExecutionModeOriginal;
if (isScripting)
{
sqlDialogSubject.ExecutionManager.ConnectionContext.CapturedSql.Clear();
}
}
//see if we need to restore the server
if (oldServer != null)
{
DataContainer.Server = oldServer;
}
}
if (isScripting)
{
executionInfo.Script = szScript;
}
}
/// <summary>
/// returns internal helper class that we delegate execution of the panels one by one when
/// we do it ourselves during scripting
/// </summary>
private ExecutionHandlerDelegate PanelExecutionHandler
{
get
{
if (this.cachedPanelExecutionHandler == null)
{
this.cachedPanelExecutionHandler = new ExecutionHandlerDelegate(this);
}
return this.cachedPanelExecutionHandler;
}
}
// #region ICustomAttributeProvider
// object[] System.Reflection.ICustomAttributeProvider.GetCustomAttributes(bool inherit)
// {
// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
// //to the derived classes
// return GetMergedArray(DoGetCustomAttributes(inherit), GetType().GetCustomAttributes(inherit));
// }
// object[] System.Reflection.ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit)
// {
// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
// //to the derived classes
// return GetMergedArray(DoGetCustomAttributes(attributeType, inherit),
// GetType().GetCustomAttributes(attributeType, inherit));
// }
// bool System.Reflection.ICustomAttributeProvider.IsDefined(Type attributeType, bool inherit)
// {
// //we merge attributes from 2 sources: type attributes and the derived classes, giving preference
// //to the derived classes
// if (!DoIsDefined(attributeType, inherit))
// {
// return GetType().IsDefined(attributeType, inherit);
// }
// else
// {
// return true;
// }
// }
// #endregion
// #region ICustomAttributeProvider helpers
// protected virtual object[] DoGetCustomAttributes(bool inherit)
// {
// return GetMergedArray(DoGetCustomAttributes(typeof(ScriptTypeAttribute), inherit),
// DoGetCustomAttributes(typeof(DialogScriptableAttribute), inherit));
// }
// protected virtual object[] DoGetCustomAttributes(Type attributeType, bool inherit)
// {
// //if the type specifies this attribute, we don't bother - it overrides
// //our behavior
// object[] typeAttribs = GetType().GetCustomAttributes(attributeType, inherit);
// if (typeAttribs != null && typeAttribs.Length > 0)
// {
// return null;
// }
// //we expose default custom attribute for script type
// if (attributeType.Equals(typeof(ScriptTypeAttribute)))
// {
// string scriptType = ScriptType;
// if (scriptType != null)
// {
// return new object[] {new ScriptTypeAttribute(scriptType)};
// }
// else
// {
// return null;
// }
// }
// else if (attributeType.Equals(typeof(DialogScriptableAttribute)))
// {
// bool canScriptToWindow = true;
// bool canScriptToFile = true;
// bool canScriptToClipboard = true;
// bool canScriptToJob = true;
// GetScriptableOptions(out canScriptToWindow,
// out canScriptToFile,
// out canScriptToClipboard,
// out canScriptToJob);
// return new object[] {new DialogScriptableAttribute(canScriptToWindow,
// canScriptToFile,
// canScriptToClipboard,
// canScriptToJob)};
// }
// return null;
// }
// protected virtual bool DoIsDefined(Type attributeType, bool inherit)
// {
// return false;
// }
// /// <summary>
// /// detects whether script types are applicable for this dlg or not. By default
// /// the framework relies on DialogScriptableAttribute set on the dlg class and won't
// /// call this method if the attribute is specified
// /// By default we assume that all script types are enabled
// /// </summary>
// /// <param name="?"></param>
// /// <param name="?"></param>
// /// <param name="?"></param>
// protected virtual void GetScriptableOptions(out bool canScriptToWindow,
// out bool canScriptToFile,
// out bool canScriptToClipboard,
// out bool canScriptToJob)
// {
// canScriptToWindow = canScriptToFile = canScriptToClipboard = canScriptToJob = true;
// }
// #endregion
protected IServiceProvider ServiceProvider
{
get
{
if (this.serviceProvider == null)
{
throw new InvalidOperationException();
}
return this.serviceProvider;
}
}
// /// <summary>
// /// returns combination of the given 2 arrays
// /// </summary>
// /// <param name="array1"></param>
// /// <param name="array2"></param>
// /// <returns></returns>
// protected object[] GetMergedArray(object[] array1, object[] array2)
// {
// if (array1 == null)
// {
// return array2;
// }
// else if (array2 == null)
// {
// return array1;
// }
// else
// {
// object[] finalReturnValue = new object[array1.Length + array2.Length];
// array1.CopyTo(finalReturnValue, 0);
// array2.CopyTo(finalReturnValue, array1.Length);
// return finalReturnValue;
// }
// }
/// <summary>
/// execution mode by default for now is success
/// </summary>
private ExecutionMode m_executionMode = ExecutionMode.Success;
/// <summary>
/// execution mode accessor
/// </summary>
protected ExecutionMode ExecutionMode
{
get
{
return m_executionMode;
}
set
{
m_executionMode = value;
}
}
public virtual ExecutionMode LastExecutionResult
{
get
{
return ExecutionMode;
}
}
/// <summary>
/// Overridable function that allow a derived class to implement
/// a finalizing action after a RunNow or RunNowAndClose where sucesfully executed
/// </summary>
/// <param name="sender"></param>
public virtual void OnTaskCompleted(object sender, ExecutionMode executionMode, RunType executionType)
{
//nothing
}
/// <summary>
/// Overridable function that allow a derived class to implement its
/// OnRunNow functionality
/// </summary>
/// <param name="sender"></param>
public virtual void OnRunNow(object sender)
{
//nothing
}
/// <summary>
/// Overridable function that allow a derived class to implement its
/// OnScript functionality.
/// </summary>
/// <param name="sender"></param>
public virtual string OnScript(object sender)
{
//redirect to the single scripting virtual method by default
return Script();
}
/// <summary>
/// derived class should override this method if it does same action for all types of scripting,
/// because all ILaunchFormHostedControl scripting methods implemented in this class simply
/// call this method
/// </summary>
/// <returns></returns>
protected virtual string Script()
{
// redirect to the RunNow method. Our host should be turning script capture on and off for
// OLAP/SQL servers and composing the text of the resulting script by itself
OnRunNow(this);
// null is a special value. It means that we want to indicate that we didn't want to generate
// script text
return null;
}
/// <summary>
/// performs custom action wen user requests a cancel
/// this is called from the UI thread and generally executes
/// smoServer.Cancel() or amoServer.Cancel() causing
/// the worker thread to inttrerupt its current action
/// </summary>
/// <param name="sender"></param>
public virtual void OnCancel(object sender)
{
if (this.dataContainer == null)
{
return;
}
if (this.dataContainer.Server != null)
{
// TODO: uncomment next line when SMO server will have support for Cancel
// this.dataContainer.Server.Cancel();
}
}
}
}

View File

@@ -0,0 +1,27 @@
//
// 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.ServiceLayer.TaskServices;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// Utility functions for working with Management classes
/// </summary>
public static class ManagementUtils
{
public static RunType asRunType(TaskExecutionMode taskExecutionMode)
{
if (taskExecutionMode == TaskExecutionMode.Script)
{
return RunType.ScriptToWindow;
}
else
{
return RunType.RunNow;
}
}
}
}

View File

@@ -0,0 +1,331 @@
//
// 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.Globalization;
using System.Collections;
using System.Threading;
using System.Text;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// Allows for the mapping of objects that implement IProgressItem to individual items in the
/// progress dialog.
/// </summary>
public class ProgressItemCollection : ICollection
{
#region internal helper classes
/// <summary>
/// Allows us to map an action to its index in the progress dialog.
/// </summary>
public class ActionIndexMap
{
/// <summary>
/// action
/// </summary>
public IProgressItem Action;
/// <summary>
/// index
/// </summary>
public int Index;
public ActionIndexMap(IProgressItem action)
{
this.Action = action;
// index isn't known yet
this.Index = -1;
}
}
#endregion
#region private data members
/// <summary>
/// list of actions we will perform.
/// </summary>
private ArrayList actions = new ArrayList();
#endregion
#region construction
public ProgressItemCollection()
{
}
#endregion
#region properties
private bool closeOnUserCancel = false;
/// <summary>
/// Indicates whether to close the dialog immediately if the user cancels an operation
/// </summary>
public bool CloseOnUserCancel
{
get
{
return closeOnUserCancel;
}
set
{
closeOnUserCancel = value;
}
}
private bool automaticClose = false;
/// <summary>
/// Indicates whether to automatically close the dialog when all actions are complete
/// successfully.
/// </summary>
public bool CloseOnSuccessfulCompletion
{
get
{
return automaticClose;
}
set
{
automaticClose = value;
}
}
private bool quitOnError = false;
/// <summary>
/// Indicates whether the operation should be terminated if any individual step fails.
/// </summary>
public bool QuitOnError
{
get
{
return this.quitOnError;
}
set
{
this.quitOnError = value;
}
}
private OperationStatus operationStatus = OperationStatus.Invalid;
/// <summary>
/// Indicates the status of the operation.
/// </summary>
public OperationStatus OperationStatus
{
get
{
return this.operationStatus;
}
}
/// <summary>
/// Progress object this action collection will work with
/// </summary>
private IProgress progress = null;
public IProgress Progress
{
get
{
return this.progress;
}
set
{
if (this.progress != value)
{
this.progress = value;
if (this.progress != null)
{
// add the actions to the progress dialog, and
// fixup our event handler
FixUpActionsToProgress();
}
}
}
}
#endregion
#region public overrides
/// <summary>
/// Generate a string representaion of this object. It will convert all of it's IProgressItem members
/// to strings in a new line.
/// </summary>
/// <returns>string description of the actions this object contains</returns>
public override string ToString()
{
// if there are no actions then just return the default ToString
if (this.actions == null || this.actions.Count == 0)
{
return base.ToString();
}
else
{
// convert all of the actions to strings on their own line
StringBuilder sb = new StringBuilder(((ActionIndexMap)actions[0]).Action.ToString());
for (int i = 1; i < this.actions.Count; i++)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "\r\n{0}", ((ActionIndexMap)actions[i]).Action.ToString());
}
return sb.ToString();
}
}
#endregion
#region ICollection implementation
/// <summary>
/// Gets the number of actions in this collection
/// </summary>
public int Count
{
get
{
return this.actions.Count;
}
}
/// <summary>
/// not supported
/// </summary>
public bool IsSynchronized
{
get
{
throw new NotSupportedException();
}
}
/// <summary>
/// not supported
/// </summary>
public object SyncRoot
{
get
{
throw new NotSupportedException();
}
}
public void CopyTo(IProgressItem[] array, int start)
{
this.actions.CopyTo(array, start);
}
public void CopyTo(Array array, int start)
{
this.actions.CopyTo(array, start);
}
public IEnumerator GetEnumerator()
{
return this.actions.GetEnumerator();
}
#endregion
#region public methods
/// <summary>
/// Add an action to the collection
/// </summary>
/// <param name="action">action to be added</param>
public void AddAction(IProgressItem action)
{
ActionIndexMap map = new ActionIndexMap(action);
this.actions.Add(map);
}
#endregion
#region internal implementation
/// <summary>
/// delegate called when the progress dialog wants us to perform work on a new thread.
/// </summary>
private void DoWorkOnThread()
{
if (this.Progress == null)
{
return;
}
try
{
System.Threading.Thread.CurrentThread.Name = "Worker thread for " + progress.GetType();
}
catch (InvalidOperationException)
{ }
// default to succeeded.
operationStatus = OperationStatus.Success;
// carry out each action.
foreach (ActionIndexMap map in this.actions)
{
// abort if the user has decided to cancel.
if (this.Progress.IsAborted)
{
this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Aborted);
operationStatus = OperationStatus.Aborted;
break;
}
ProgressStatus stepStatus = ProgressStatus.Invalid;
try
{
// perform the action.
stepStatus = map.Action.DoAction(this, map.Index);
this.Progress.UpdateActionStatus(map.Index, stepStatus);
}
catch (Exception e)
{
// fail the step with errors, add the error messages to the control.
this.Progress.AddActionException(map.Index, e);
this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Error);
stepStatus = ProgressStatus.Error;
}
if (stepStatus == ProgressStatus.Error)
{
// see if we're supposed to fail if any step fails
if (this.QuitOnError == true)
{
// fail and quit
this.operationStatus = OperationStatus.Error;
break;
}
else
{
this.operationStatus = OperationStatus.CompletedWithErrors;
}
}
else if (stepStatus != ProgressStatus.Success)
{
this.operationStatus = OperationStatus.CompletedWithErrors;
}
}
// tell the dialog we're finishing.
this.Progress.WorkerThreadExiting(operationStatus);
// close the dialog if asked to. We have to put this after
// the WorkerThreadExiting call because the progress dialog
// won't allow itself to be closed until worker thread says
// it's finished.
if ((this.CloseOnSuccessfulCompletion && (this.operationStatus == OperationStatus.Success)) ||
(this.CloseOnUserCancel && this.Progress.IsAborted))
{
//((Form)this.Progress).BeginInvoke(new CloseProgressWindowCallback(CloseProgressWindowHandler), new object[] { this.Progress });
}
}
private delegate void CloseProgressWindowCallback(IProgress progress);
private void CloseProgressWindowHandler(IProgress progress)
{
}
/// <summary>
/// Adds the actions to an IProgress interface.
/// </summary>
private void FixUpActionsToProgress()
{
if (this.Progress == null)
{
return;
}
// add actions
foreach (ActionIndexMap map in this.actions)
{
map.Index = this.Progress.AddAction(map.Action.ToString());
}
// add our delegate
this.Progress.WorkerThreadStart = new ThreadStart(this.DoWorkOnThread);
}
#endregion
}
}

View File

@@ -0,0 +1,301 @@
//
// 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.Drawing;
using System.Threading;
using System.Runtime.InteropServices;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// Enumeration for status of individual actions
/// </summary>
public enum ProgressStatus
{
Invalid = -1,
NotStarted, // Not started
InProgress, // In progress
Success, // Completed
SuccessWithInfo, // Completed, display additional info
Warning, // Completed with warning, display exceptions
Error, // Not completed because of error, display exceptions
Aborted, // Aborted
RolledBack, // Rolled back because of a subsequent error
StatusCount // = Number of status values - For validation only
}
/// <summary>
/// Enumeration for status of the overall operation
/// </summary>
public enum OperationStatus
{
Invalid = -1,
InProgress, // In progress
Success, // Completed successfully
CompletedWithErrors, // Completed with non-fatal errors
Error, // Not completed because of error
Aborted, // Abort complete
StatusCount // = Number of status values - For validation only
}
/// <summary>
/// Interface defining core functionality of a progress control container
///
/// NOTE: Refer to the comments for each method/property individually to determine
/// whether it is thread-safe and may be used from the worker thread. Also note
/// that some members are asynchronous.
/// </summary>
public interface IProgress
{
//-----------
// Properties
//-----------
/// <summary>
/// The property that determines if the user should be allowed to abort the
/// operation. The default value is true.
///
/// NOTE: This property is not thread safe. Set from UI thread.
/// </summary>
bool AllowAbort
{
get;
set;
}
/// <summary>
/// The confirmation prompt to display if the user hits the Abort button.
/// The abort prompt should be worded such that the abort is confirmed
/// if the user hits "Yes".
///
/// NOTE: This property is not thread safe. Set from UI thread.
/// </summary>
string AbortPrompt
{
get;
set;
}
/// <summary>
/// The ThreadStart delegate. The method referenced by this delegate is run by
/// the worker thread to perform the operation.
///
/// NOTE: This property is not thread safe. Set from UI thread.
/// </summary>
ThreadStart WorkerThreadStart
{
get;
set;
}
/// <summary>
/// Aborted status of the operation.
///
/// NOTE: This property is thread safe and may be called from the worker
/// thread. Accessing this property may cause caller to block if UI
/// is waiting for user confirmation of abort.
/// </summary>
bool IsAborted
{
get;
}
/// <summary>
/// This property determines whether updates should be allowed for
/// actions. If this is set to false, any calls that are made
/// to add or update an action are ignored. The default value is true.
///
/// NOTE: This property is thread safe and may be called from the worker
/// thread.
/// </summary>
bool ActionUpdateEnabled
{
get;
set;
}
//--------
// Methods
//--------
/// <summary>
/// Add an action to the displayed list of actions. Actions are
/// displayed in the order they are added. An action can be referenced
/// in future calls using the zero-based index that is returned.
///
/// The description must be a non-empty string.
///
/// NOTE: This method is not thread safe. Call from the UI thread.
/// </summary>
/// <param name="description">Description of the action</param>
/// <returns>The index of the newly added action.</returns>
int AddAction(string description);
/// <summary>
/// Add an action to the displayed list of actions. This is meant
/// to be called from the worker thread to add an action after
/// the operation is already in progress.
///
/// Actions are displayed in the order they are added. Use the
/// zero-based index of the action based on past actions added
/// to reference it in future calls. The description must be a
/// non-empty string.
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="description">Description of the action</param>
void AddActionDynamic(string description);
/// <summary>
/// Update the description of an action
///
/// The description must be a non-empty string.
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="description">New description of the action</param>
void UpdateActionDescription(int actionIndex, string description);
/// <summary>
/// Update the status of an action
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="status">New status of the action</param>
void UpdateActionStatus(int actionIndex, ProgressStatus status);
/// <summary>
/// Update the progress of an action in terms of percentage complete
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="percentComplete">Percentage of the action that is complete (0-100)</param>
void UpdateActionProgress(int actionIndex, int percentComplete);
/// <summary>
/// Update the progress of an action with a text description of
/// the progress
///
/// The description must be a non-empty string.
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="description">Description of progress</param>
void UpdateActionProgress(int actionIndex, string description);
/// <summary>
/// Add an exception to an action
///
/// Exceptions are displayed in the action grid only for actions
/// with "Error" or "Warning" status.
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="e">Exception to be added</param>
void AddActionException(int actionIndex, Exception e);
/// <summary>
/// Add an info string to an action in the progress report control
///
/// Information strings are displayed in the action grid only for
/// actions with "SuccessWithInfo" status. The info string must
/// be a non-empty string. It should not be formatted or contain
/// newline characters.
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="actionIndex">Index of the action</param>
/// <param name="infoString">Information string to be added</param>
void AddActionInfoString(int actionIndex, string infoString);
/// <summary>
/// Call this method when the worker thread performing the operation
/// is about to exit. The final result of the operation is supplied in
/// the form of a OperationStatus value.
///
/// NOTE: This method is thread safe and asynchronous. It may be
/// called from the worker thread.
/// </summary>
/// <param name="result">Result of the operation</param>
void WorkerThreadExiting(OperationStatus result);
}
/// <summary>
/// Enumeration for status of the progress report control w.r.t the operation
/// </summary>
[System.Runtime.InteropServices.ComVisible(false)]
public enum ProgressCtrlStatus
{
Invalid = -1,
InProgress, // In progress
Success, // Completed successfully
CompletedWithErrors, // Completed with non-fatal errors
Error, // Not completed because of error
Aborting, // User clicked "Abort", aborting operation
Aborted, // Abort complete
Closed, // User clicked "Close"
StatusCount // = Number of status values - For validation only
}
/// <summary>
/// Delegate used with ProgressCtrlStatusChanged event.
/// </summary>
public delegate void ProgressCtrlStatusChangedEventHandler(object source, ProgressCtrlStatusChangedEventArgs e);
/// <summary>
/// EventArgs class for use with ProgressCtrlStatusChanged event
/// </summary>
sealed public class ProgressCtrlStatusChangedEventArgs : EventArgs
{
//------------------
// Public Properties
//------------------
public ProgressCtrlStatus Status
{
get { return m_status; }
set { m_status = value; }
}
//-------------
// Private Data
//-------------
private ProgressCtrlStatus m_status = ProgressCtrlStatus.Invalid;
}
// Enumeration for progress action grid columns
internal enum ProgressActionColumn
{
Invalid = -1,
ActionStatusBitmap,
ActionDescription,
ActionStatusText,
ActionMessage,
ActionColumnCount // = Number of columns - For validation only
}
// Enumeration for progress action display filter
internal enum ProgressActionDisplayMode
{
Invalid = -1,
DisplayAllActions,
DisplayErrors,
DisplaySuccess,
DisplayWarnings,
ActionDisplayModeCount // = Number of display modes - For validation only
}
}

View File

@@ -0,0 +1,22 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// what type of actions does the worker know to execute
/// </summary>
public enum RunType
{
RunNow = 0,
RunNowAndExit,
ScriptToFile,
ScriptToWindow,
ScriptToClipboard,
ScriptToJob
}
}

View File

@@ -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;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
/// <summary>
/// Custom attribute that can be applied on particular DB commander to
/// indicate whether the base class should switch SMO servers before
/// execution or not.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class ServerSwitchingAttribute : Attribute
{
private bool needToSwitch = false;
private ServerSwitchingAttribute() {}
public ServerSwitchingAttribute(bool needToSwitchServer)
{
this.needToSwitch = needToSwitchServer;
}
public bool NeedToSwtichServerObject
{
get
{
return this.needToSwitch;
}
}
}
}

View File

@@ -0,0 +1,27 @@
// //
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
// //
// using System;
// /// <summary>
// /// Defines static values for both Yukon and Shiloh
// /// </summary>
// namespace Microsoft.SqlTools.ServiceLayer.Management
// {
// public class SqlLimits
// {
// internal SqlLimits()
// {
// }
// //Define Version 9 Limits
// //Currently MAX is the same for both nVarchar and Varchar.
// public static readonly int VarcharMax = 1073741824;
// public static readonly int SysName = 128;
// //Define Pre-Version 9 Limits
// public static readonly int CommandDimensionMaxLength=3200;
// }
// }