mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-22 01:25:44 -05:00
Fix tools service crash + improve server edition naming (#506)
* Fix https://github.com/Microsoft/vscode-mssql/issues/986 and add better server edition naming - Fixed a number of issues related to the binding queue, specifically the fact that it didn't capture exceptions that occurred on the binding thread. This caused the thread to crash the service. - The root cause of the error was when you get a connection error during init of the SmoMetadataProvider which threw an exception. Because of this no Binder was created, and the code would null ref later during processing - Added logic to handle null ref issue and other related code - Added a unit test for the new error handling path, and supported still returning some value in this case - Separately, have a fix for an issue where the edition is shown as "SQL Azure" for all Azure connections. Fixing to be "Azure SQL DB" for this case and handle when the database reports as DW or Stretch instead. This maps better to users concept of what the edition should be and avoids returning what is an outdated term. * Messed up the test - fixing this by returning the expected return object
This commit is contained in:
@@ -29,6 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
public class ConnectionService
|
||||
{
|
||||
public const string AdminConnectionPrefix = "ADMIN:";
|
||||
private const string SqlAzureEdition = "SQL Azure";
|
||||
|
||||
/// <summary>
|
||||
/// Singleton service instance
|
||||
@@ -456,7 +457,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
EngineEditionId = serverInfo.EngineEditionId,
|
||||
ServerVersion = serverInfo.ServerVersion,
|
||||
ServerLevel = serverInfo.ServerLevel,
|
||||
ServerEdition = serverInfo.ServerEdition,
|
||||
ServerEdition = MapServerEdition(serverInfo),
|
||||
IsCloud = serverInfo.IsCloud,
|
||||
AzureVersion = serverInfo.AzureVersion,
|
||||
OsVersion = serverInfo.OsVersion,
|
||||
@@ -474,6 +475,31 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
return response;
|
||||
}
|
||||
|
||||
private string MapServerEdition(ReliableConnectionHelper.ServerInfo serverInfo)
|
||||
{
|
||||
string serverEdition = serverInfo.ServerEdition;
|
||||
if (string.IsNullOrWhiteSpace(serverEdition))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
if (SqlAzureEdition.Equals(serverEdition, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
switch(serverInfo.EngineEditionId)
|
||||
{
|
||||
case (int) DatabaseEngineEdition.SqlDataWarehouse:
|
||||
serverEdition = SR.AzureSqlDwEdition;
|
||||
break;
|
||||
case (int) DatabaseEngineEdition.SqlStretchDatabase:
|
||||
serverEdition = SR.AzureSqlStretchEdition;
|
||||
break;
|
||||
default:
|
||||
serverEdition = SR.AzureSqlDbEdition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return serverEdition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to create and open a connection with the given ConnectParams.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
{
|
||||
@@ -64,6 +65,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
string key,
|
||||
Func<IBindingContext, CancellationToken, object> bindOperation,
|
||||
Func<IBindingContext, object> timeoutOperation = null,
|
||||
Func<Exception, object> errorHandler = null,
|
||||
int? bindingTimeout = null,
|
||||
int? waitForLockTimeout = null)
|
||||
{
|
||||
@@ -78,6 +80,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
Key = key,
|
||||
BindOperation = bindOperation,
|
||||
TimeoutOperation = timeoutOperation,
|
||||
ErrorHandler = errorHandler,
|
||||
BindingTimeout = bindingTimeout,
|
||||
WaitForLockTimeout = waitForLockTimeout
|
||||
};
|
||||
@@ -92,6 +95,23 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
return queueItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a particular binding context is connected or not
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public bool IsBindingContextConnected(string key)
|
||||
{
|
||||
lock (this.bindingContextLock)
|
||||
{
|
||||
IBindingContext context;
|
||||
if (this.BindingContextMap.TryGetValue(key, out context))
|
||||
{
|
||||
return context.IsConnected;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a binding context for the provided context key
|
||||
/// </summary>
|
||||
@@ -270,9 +290,20 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
// run the operation in a separate thread
|
||||
var bindThread = new Thread(() =>
|
||||
{
|
||||
result = queueItem.BindOperation(
|
||||
bindingContext,
|
||||
cancelToken.Token);
|
||||
try
|
||||
{
|
||||
result = queueItem.BindOperation(
|
||||
bindingContext,
|
||||
cancelToken.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Error, "Unexpected exception on the binding queue: " + ex.ToString());
|
||||
if (queueItem.ErrorHandler != null)
|
||||
{
|
||||
result = queueItem.ErrorHandler(ex);
|
||||
}
|
||||
}
|
||||
}, BindingQueue<T>.QueueThreadStackSize);
|
||||
bindThread.Start();
|
||||
|
||||
@@ -282,7 +313,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
queueItem.Result = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
cancelToken.Cancel();
|
||||
|
||||
// if the task didn't complete then call the timeout callback
|
||||
@@ -298,7 +329,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
// wait for the operation to complete before releasing the lock
|
||||
bindThread.Join();
|
||||
bindingContext.BindingLock.Set();
|
||||
});
|
||||
}).ContinueWithOnFaulted(t => Logger.Write(LogLevel.Error, "Binding queue threw exception " + t.Exception.ToString()));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -104,6 +104,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Completion
|
||||
{
|
||||
// return the default list if the connected bind fails
|
||||
return CreateDefaultCompletionItems(scriptParseInfo, scriptDocumentInfo, useLowerCaseSuggestions);
|
||||
},
|
||||
errorHandler: ex =>
|
||||
{
|
||||
// return the default list if an unexpected exception occurs
|
||||
return CreateDefaultCompletionItems(scriptParseInfo, scriptDocumentInfo, useLowerCaseSuggestions);
|
||||
});
|
||||
return queueItem;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
string key,
|
||||
Func<IBindingContext, CancellationToken, object> bindOperation,
|
||||
Func<IBindingContext, object> timeoutOperation = null,
|
||||
Func<Exception, object> errorHandler = null,
|
||||
int? bindingTimeout = null,
|
||||
int? waitForLockTimeout = null);
|
||||
}
|
||||
|
||||
@@ -794,10 +794,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
|
||||
List<ParseResult> parseResults = new List<ParseResult>();
|
||||
parseResults.Add(parseResult);
|
||||
bindingContext.Binder.Bind(
|
||||
parseResults,
|
||||
connInfo.ConnectionDetails.DatabaseName,
|
||||
BindMode.Batch);
|
||||
if (bindingContext.IsConnected && bindingContext.Binder != null)
|
||||
{
|
||||
bindingContext.Binder.Bind(
|
||||
parseResults,
|
||||
connInfo.ConnectionDetails.DatabaseName,
|
||||
BindMode.Batch);
|
||||
}
|
||||
}
|
||||
catch (ConnectionException)
|
||||
{
|
||||
@@ -856,8 +859,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
try
|
||||
{
|
||||
scriptInfo.ConnectionKey = this.BindingQueue.AddConnectionContext(info, "languageService");
|
||||
scriptInfo.IsConnected = true;
|
||||
|
||||
scriptInfo.IsConnected = this.BindingQueue.IsBindingContextConnected(scriptInfo.ConnectionKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -917,40 +919,42 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
ParseResult parseResult = Parser.Parse(
|
||||
"select ",
|
||||
bindingContext.ParseOptions);
|
||||
if (bindingContext.IsConnected && bindingContext.Binder != null)
|
||||
{
|
||||
List<ParseResult> parseResults = new List<ParseResult>();
|
||||
parseResults.Add(parseResult);
|
||||
bindingContext.Binder.Bind(
|
||||
parseResults,
|
||||
info.ConnectionDetails.DatabaseName,
|
||||
BindMode.Batch);
|
||||
|
||||
List<ParseResult> parseResults = new List<ParseResult>();
|
||||
parseResults.Add(parseResult);
|
||||
bindingContext.Binder.Bind(
|
||||
parseResults,
|
||||
info.ConnectionDetails.DatabaseName,
|
||||
BindMode.Batch);
|
||||
// get the completion list from SQL Parser
|
||||
var suggestions = Resolver.FindCompletions(
|
||||
parseResult, 1, 8,
|
||||
bindingContext.MetadataDisplayInfoProvider);
|
||||
|
||||
// get the completion list from SQL Parser
|
||||
var suggestions = Resolver.FindCompletions(
|
||||
parseResult, 1, 8,
|
||||
bindingContext.MetadataDisplayInfoProvider);
|
||||
// this forces lazy evaluation of the suggestion metadata
|
||||
AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 8, 8);
|
||||
|
||||
// this forces lazy evaluation of the suggestion metadata
|
||||
AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 8, 8);
|
||||
parseResult = Parser.Parse(
|
||||
"exec ",
|
||||
bindingContext.ParseOptions);
|
||||
|
||||
parseResult = Parser.Parse(
|
||||
"exec ",
|
||||
bindingContext.ParseOptions);
|
||||
parseResults = new List<ParseResult>();
|
||||
parseResults.Add(parseResult);
|
||||
bindingContext.Binder.Bind(
|
||||
parseResults,
|
||||
info.ConnectionDetails.DatabaseName,
|
||||
BindMode.Batch);
|
||||
|
||||
parseResults = new List<ParseResult>();
|
||||
parseResults.Add(parseResult);
|
||||
bindingContext.Binder.Bind(
|
||||
parseResults,
|
||||
info.ConnectionDetails.DatabaseName,
|
||||
BindMode.Batch);
|
||||
// get the completion list from SQL Parser
|
||||
suggestions = Resolver.FindCompletions(
|
||||
parseResult, 1, 6,
|
||||
bindingContext.MetadataDisplayInfoProvider);
|
||||
|
||||
// get the completion list from SQL Parser
|
||||
suggestions = Resolver.FindCompletions(
|
||||
parseResult, 1, 6,
|
||||
bindingContext.MetadataDisplayInfoProvider);
|
||||
|
||||
// this forces lazy evaluation of the suggestion metadata
|
||||
AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 6, 6);
|
||||
// this forces lazy evaluation of the suggestion metadata
|
||||
AutoCompleteHelper.ConvertDeclarationsToCompletionItems(suggestions, 1, 6, 6);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -1103,6 +1107,16 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
Message = SR.PeekDefinitionTimedoutError,
|
||||
Locations = null
|
||||
};
|
||||
},
|
||||
errorHandler: ex =>
|
||||
{
|
||||
// return error result
|
||||
return new DefinitionResult
|
||||
{
|
||||
IsErrorResult = true,
|
||||
Message = ex.Message,
|
||||
Locations = null
|
||||
};
|
||||
});
|
||||
|
||||
// wait for the queue item
|
||||
|
||||
@@ -36,6 +36,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
/// </summary>
|
||||
public Func<IBindingContext, object> TimeoutOperation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the operation to call if the bind operation encounters an unexpected exception.
|
||||
/// Supports returning an object in case of the exception occurring since in some cases we need to be
|
||||
/// tolerant of error cases and still return some value
|
||||
/// </summary>
|
||||
public Func<Exception, object> ErrorHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an event to signal when this queue item has been processed
|
||||
/// </summary>
|
||||
|
||||
@@ -77,6 +77,30 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string AzureSqlDbEdition
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.AzureSqlDbEdition);
|
||||
}
|
||||
}
|
||||
|
||||
public static string AzureSqlDwEdition
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.AzureSqlDwEdition);
|
||||
}
|
||||
}
|
||||
|
||||
public static string AzureSqlStretchEdition
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.AzureSqlStretchEdition);
|
||||
}
|
||||
}
|
||||
|
||||
public static string QueryServiceCancelAlreadyCompleted
|
||||
{
|
||||
get
|
||||
@@ -3652,6 +3676,15 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string ConnectionParamsValidateNullSqlAuth = "ConnectionParamsValidateNullSqlAuth";
|
||||
|
||||
|
||||
public const string AzureSqlDbEdition = "AzureSqlDbEdition";
|
||||
|
||||
|
||||
public const string AzureSqlDwEdition = "AzureSqlDwEdition";
|
||||
|
||||
|
||||
public const string AzureSqlStretchEdition = "AzureSqlStretchEdition";
|
||||
|
||||
|
||||
public const string QueryServiceCancelAlreadyCompleted = "QueryServiceCancelAlreadyCompleted";
|
||||
|
||||
|
||||
|
||||
@@ -166,6 +166,18 @@
|
||||
<comment>.
|
||||
Parameters: 0 - component (string) </comment>
|
||||
</data>
|
||||
<data name="AzureSqlDbEdition" xml:space="preserve">
|
||||
<value>Azure SQL DB</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="AzureSqlDwEdition" xml:space="preserve">
|
||||
<value>Azure SQL Data Warehouse</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="AzureSqlStretchEdition" xml:space="preserve">
|
||||
<value>Azure SQL Stretch Database</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="QueryServiceCancelAlreadyCompleted" xml:space="preserve">
|
||||
<value>The query has already completed, it cannot be cancelled</value>
|
||||
<comment></comment>
|
||||
|
||||
@@ -47,6 +47,11 @@ ConnectionParamsValidateNullServerName = ServerName cannot be null or empty
|
||||
|
||||
ConnectionParamsValidateNullSqlAuth(string component) = {0} cannot be null or empty when using SqlLogin authentication
|
||||
|
||||
### General connection service strings
|
||||
AzureSqlDbEdition = Azure SQL DB
|
||||
AzureSqlDwEdition = Azure SQL Data Warehouse
|
||||
AzureSqlStretchEdition = Azure SQL Stretch Database
|
||||
|
||||
############################################################################
|
||||
# Query Execution Service
|
||||
|
||||
|
||||
@@ -2280,6 +2280,21 @@
|
||||
<target state="new">Never</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="AzureSqlDbEdition">
|
||||
<source>Azure SQL DB</source>
|
||||
<target state="new">Azure SQL DB</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="AzureSqlDwEdition">
|
||||
<source>Azure SQL Data Warehouse</source>
|
||||
<target state="new">Azure SQL Data Warehouse</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="AzureSqlStretchEdition">
|
||||
<source>Azure SQL Stretch Database</source>
|
||||
<target state="new">Azure SQL Stretch Database</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -238,7 +238,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
string script = string.Empty;
|
||||
ScriptingObject scriptingObject = parameters.ScriptingObjects[0];
|
||||
try
|
||||
{
|
||||
{
|
||||
Server server = new Server(bindingContext.ServerConnection);
|
||||
server.DefaultTextMode = true;
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="TestLocalizationConstant" xml:space="preserve">
|
||||
<value>ES_LOCALIZATION</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -211,6 +211,7 @@ GO";
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<Func<IBindingContext, CancellationToken, object>>(),
|
||||
It.IsAny<Func<IBindingContext, object>>(),
|
||||
It.IsAny<Func<Exception, object>>(),
|
||||
It.IsAny<int?>(),
|
||||
It.IsAny<int?>()))
|
||||
.Callback<string, Func<IBindingContext, CancellationToken, object>, Func<IBindingContext, object>, int?, int?>(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.SmoMetadataProvider;
|
||||
@@ -133,6 +134,35 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.LanguageServer
|
||||
Assert.False(this.isCancelationRequested);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues a single task
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void QueueWithUnhandledExceptionTest()
|
||||
{
|
||||
InitializeTestSettings();
|
||||
ManualResetEvent mre = new ManualResetEvent(false);
|
||||
bool isExceptionHandled = false;
|
||||
object defaultReturnObject = new object();
|
||||
var queueItem = this.bindingQueue.QueueBindingOperation(
|
||||
key: "testkey",
|
||||
bindOperation: (context, CancellationToken) => throw new Exception("Unhandled!!"),
|
||||
timeoutOperation: TestTimeoutOperation,
|
||||
errorHandler: (exception) => {
|
||||
isExceptionHandled = true;
|
||||
mre.Set();
|
||||
return defaultReturnObject;
|
||||
});
|
||||
|
||||
mre.WaitOne(10000);
|
||||
|
||||
this.bindingQueue.StopQueueProcessor(15000);
|
||||
|
||||
Assert.True(isExceptionHandled);
|
||||
var result = queueItem.GetResultAsT<object>();
|
||||
Assert.Equal(defaultReturnObject, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queue a 100 short tasks
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user