Check database case sensitivity for peek definition token matching (#227)

* Check database case sensitivity before peek definition token matching

* Add sr gen

* add tests for ignore case

* remove sr.Designer
This commit is contained in:
Sharon Ravindran
2017-02-08 12:34:01 -08:00
committed by GitHub
parent 50195faced
commit b10d6bdfdc
6 changed files with 107 additions and 42 deletions

View File

@@ -72,10 +72,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
try try
{ {
// Reuse existing connection // Reuse existing connection
Server server = new Server(this.serverConnection); Server server = new Server(this.serverConnection);
// The default database name is the database name of the server connection // The default database name is the database name of the server connection
string dbName = this.serverConnection.DatabaseName; string dbName = this.serverConnection.DatabaseName;
if (this.connectionInfo != null) if (this.connectionInfo != null)
{ {
// If there is a query DbConnection, use that connection to get the database name // If there is a query DbConnection, use that connection to get the database name
@@ -86,10 +86,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
if (!string.IsNullOrEmpty(connection.Database)) if (!string.IsNullOrEmpty(connection.Database))
{ {
dbName = connection.Database; dbName = connection.Database;
} }
} }
} }
this.database = new Database(server, dbName); this.database = new Database(server, dbName);
this.database.Refresh();
} }
catch (ConnectionFailureException cfe) catch (ConnectionFailureException cfe)
{ {
@@ -146,8 +147,13 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
continue; continue;
} }
// if declarartionItem matches the selected token, script SMO using that type if (this.Database == null)
if (declarationItem.Title.Equals(tokenText)) {
return GetDefinitionErrorResult(SR.PeekDefinitionDatabaseError);
}
StringComparison caseSensitivity = this.Database.CaseSensitive ? StringComparison.Ordinal : 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); return GetDefinitionUsingDeclarationType(declarationItem.Type, declarationItem.DatabaseQualifiedName, tokenText, schemaName);
} }
@@ -172,7 +178,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// <returns></returns> /// <returns></returns>
internal DefinitionResult GetDefinitionUsingQuickInfoText(string quickInfoText, string tokenText, string schemaName) internal DefinitionResult GetDefinitionUsingQuickInfoText(string quickInfoText, string tokenText, string schemaName)
{ {
string tokenType = GetTokenTypeFromQuickInfo(quickInfoText, tokenText); if (this.Database == null)
{
return GetDefinitionErrorResult(SR.PeekDefinitionDatabaseError);
}
StringComparison caseSensitivity = this.Database.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
string tokenType = GetTokenTypeFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
if (tokenType != null) if (tokenType != null)
{ {
if (sqlScriptGettersFromQuickInfo.ContainsKey(tokenType.ToLowerInvariant())) if (sqlScriptGettersFromQuickInfo.ContainsKey(tokenType.ToLowerInvariant()))
@@ -183,7 +194,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// If all fails, the default schema name is assumed to be "dbo" // If all fails, the default schema name is assumed to be "dbo"
if ((connectionInfo != null && connectionInfo.ConnectionDetails.AuthenticationType.Equals(Constants.SqlLoginAuthenticationType)) && string.IsNullOrEmpty(schemaName)) if ((connectionInfo != null && connectionInfo.ConnectionDetails.AuthenticationType.Equals(Constants.SqlLoginAuthenticationType)) && string.IsNullOrEmpty(schemaName))
{ {
string fullObjectName = this.GetFullObjectNameFromQuickInfo(quickInfoText, tokenText); string fullObjectName = this.GetFullObjectNameFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText); schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
} }
Location[] locations = GetSqlObjectDefinition( Location[] locations = GetSqlObjectDefinition(
@@ -377,8 +388,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
/// <param name="quickInfoText">QuickInfo Text for this token</param> /// <param name="quickInfoText">QuickInfo Text for this token</param>
/// <param name="tokenText">Token Text</param> /// <param name="tokenText">Token Text</param>
/// <param name="caseSensitivity">StringComparison enum</param>
/// <returns></returns> /// <returns></returns>
internal string GetFullObjectNameFromQuickInfo(string quickInfoText, string tokenText) internal string GetFullObjectNameFromQuickInfo(string quickInfoText, string tokenText, StringComparison caseSensitivity)
{ {
if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText)) if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText))
{ {
@@ -386,7 +398,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
// extract full object name from quickInfo text // extract full object name from quickInfo text
string[] tokens = quickInfoText.Split(' '); string[] tokens = quickInfoText.Split(' ');
List<string> tokenList = tokens.Where(el => el.Contains(tokenText)).ToList(); List<string> tokenList = tokens.Where(el => el.IndexOf(tokenText, caseSensitivity) >= 0).ToList();
return (tokenList?.Count() > 0) ? tokenList[0] : null; return (tokenList?.Count() > 0) ? tokenList[0] : null;
} }
@@ -395,8 +407,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
/// <param name="quickInfoText">QuickInfo Text for this token</param> /// <param name="quickInfoText">QuickInfo Text for this token</param>
/// <param name="tokenText"Token Text></param> /// <param name="tokenText"Token Text></param>
/// <param name="caseSensitivity">StringComparison enum</param>
/// <returns></returns> /// <returns></returns>
internal string GetTokenTypeFromQuickInfo(string quickInfoText, string tokenText) internal string GetTokenTypeFromQuickInfo(string quickInfoText, string tokenText, StringComparison caseSensitivity)
{ {
if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText)) if (string.IsNullOrEmpty(quickInfoText) || string.IsNullOrEmpty(tokenText))
{ {
@@ -404,7 +417,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
// extract string denoting the token type from quickInfo text // extract string denoting the token type from quickInfo text
string[] tokens = quickInfoText.Split(' '); string[] tokens = quickInfoText.Split(' ');
List<int> indexList = tokens.Select((s, i) => new { i, s }).Where(el => (el.s).Contains(tokenText)).Select(el => el.i).ToList(); 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; return (indexList?.Count() > 0) ? String.Join(" ", tokens.Take(indexList[0])) : null;
} }

View File

@@ -381,6 +381,14 @@ namespace Microsoft.SqlTools.ServiceLayer
} }
} }
public static string PeekDefinitionDatabaseError
{
get
{
return Keys.GetString(Keys.PeekDefinitionDatabaseError);
}
}
public static string PeekDefinitionNotConnectedError public static string PeekDefinitionNotConnectedError
{ {
get get
@@ -643,6 +651,9 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string PeekDefinitionNoResultsError = "PeekDefinitionNoResultsError"; public const string PeekDefinitionNoResultsError = "PeekDefinitionNoResultsError";
public const string PeekDefinitionDatabaseError = "PeekDefinitionDatabaseError";
public const string PeekDefinitionNotConnectedError = "PeekDefinitionNotConnectedError"; public const string PeekDefinitionNotConnectedError = "PeekDefinitionNotConnectedError";

View File

@@ -343,6 +343,10 @@
<value>No results were found.</value> <value>No results were found.</value>
<comment></comment> <comment></comment>
</data> </data>
<data name="PeekDefinitionDatabaseError" xml:space="preserve">
<value>No database object was retrieved.</value>
<comment></comment>
</data>
<data name="PeekDefinitionNotConnectedError" xml:space="preserve"> <data name="PeekDefinitionNotConnectedError" xml:space="preserve">
<value>Please connect to a server.</value> <value>Please connect to a server.</value>
<comment></comment> <comment></comment>

View File

@@ -160,6 +160,8 @@ PeekDefinitionError(string errorMessage) = An unexpected error occurred during P
PeekDefinitionNoResultsError = No results were found. PeekDefinitionNoResultsError = No results were found.
PeekDefinitionDatabaseError = No database object was retrieved.
PeekDefinitionNotConnectedError = Please connect to a server. PeekDefinitionNotConnectedError = Please connect to a server.
PeekDefinitionTimedoutError = Operation timed out. PeekDefinitionTimedoutError = Operation timed out.

View File

@@ -29,24 +29,24 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.LanguageServices
private const string OwnerUri = "testFile1"; private const string OwnerUri = "testFile1";
private const string ReturnTableFunctionName = "pd_returnTable"; private const string ReturnTableFunctionName = "pd_returnTable";
private const string ReturnTableTableFunctionQuery = @" private const string ReturnTableTableFunctionQuery = @"
CREATE FUNCTION [dbo].[" + ReturnTableFunctionName + @"] () CREATE FUNCTION [dbo].[" + ReturnTableFunctionName + @"] ()
RETURNS TABLE RETURNS TABLE
AS AS
RETURN RETURN
( (
select * from master.dbo.spt_monitor select * from master.dbo.spt_monitor
); );
GO"; GO";
private const string AddTwoFunctionName = "pd_addTwo"; private const string AddTwoFunctionName = "pd_addTwo";
private const string AddTwoFunctionQuery = @" private const string AddTwoFunctionQuery = @"
CREATE FUNCTION[dbo].[" + AddTwoFunctionName + @"](@number int) CREATE FUNCTION[dbo].[" + AddTwoFunctionName + @"](@number int)
RETURNS int RETURNS int
AS AS
BEGIN BEGIN
RETURN @number + 2; RETURN @number + 2;
END; END;
GO"; GO";
@@ -147,11 +147,14 @@ GO";
/// Test GetDefinition with an unsupported type(schema - dbo). Expect a error result. /// Test GetDefinition with an unsupported type(schema - dbo). Expect a error result.
/// </summary> /// </summary>
[Fact] [Fact]
public void GetUnsupportedDefinitionErrorTest() public async Task GetUnsupportedDefinitionErrorTest()
{ {
TestConnectionResult connectionResult = TestObjects.InitLiveConnectionInfo();
connectionResult.ScriptFile.Contents = "select * from dbo.func ()";
string ownerUri = connectionResult.ScriptFile.ClientFilePath;
TextDocumentPosition textDocument = new TextDocumentPosition TextDocumentPosition textDocument = new TextDocumentPosition
{ {
TextDocument = new TextDocumentIdentifier { Uri = OwnerUri }, TextDocument = new TextDocumentIdentifier { Uri = ownerUri },
Position = new Position Position = new Position
{ {
Line = 0, Line = 0,
@@ -159,17 +162,16 @@ GO";
Character = 15 Character = 15
} }
}; };
connectionResult.TextDocumentPosition = textDocument;
TestConnectionResult connectionResult = TestObjects.InitLiveConnectionInfo(); var languageService = LanguageService.Instance;
connectionResult.ScriptFile.Contents = "select * from dbo.func ()"; await languageService.UpdateLanguageServiceOnConnection(connectionResult.ConnectionInfo);
var languageService = new LanguageService(); ScriptParseInfo parseInfo = languageService.GetScriptParseInfo(connectionResult.ScriptFile.ClientFilePath);
ScriptParseInfo scriptInfo = new ScriptParseInfo { IsConnected = true }; Assert.NotNull(parseInfo);
languageService.ScriptParseInfoMap.Add(OwnerUri, scriptInfo);
// When I call the language service // When I call the language service
var result = languageService.GetDefinition(textDocument, connectionResult.ScriptFile, connectionResult.ConnectionInfo); var result = languageService.GetDefinition(textDocument, connectionResult.ScriptFile, connectionResult.ConnectionInfo);
// Then I expect null locations and an error to be reported // Then I expect non null result with error flag set
Assert.NotNull(result); Assert.NotNull(result);
Assert.True(result.IsErrorResult); Assert.True(result.IsErrorResult);
} }
@@ -642,7 +644,7 @@ GO";
/// <summary> /// <summary>
/// Test get definition using quickInfo text for a view object with active connection /// Test get definition using quickInfo text for a view object with active connection
/// Expect a non-null result with location /// Expect a non-null result with location
/// </summary> /// </summary>
[Fact] [Fact]
public void GetDefinitionUsingQuickInfoTextWithNonexistentObjectTest() public void GetDefinitionUsingQuickInfoTextWithNonexistentObjectTest()

View File

@@ -233,7 +233,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
PeekDefinition peekDefinition = new PeekDefinition(null, null); PeekDefinition peekDefinition = new PeekDefinition(null, null);
string objectName = "testTable"; string objectName = "testTable";
string quickInfoText = "table master.dbo.testTable"; string quickInfoText = "table master.dbo.testTable";
string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName); string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
string expected = "master.dbo.testTable";
Assert.Equal(expected, result);
}
/// <summary>
/// Test extracting the full object name from quickInfoText with case insensitive comparison.
/// Given a valid object name string and a vaild quickInfo string containing the object name
/// Expect the full object name (database.schema.objectName)
/// </summary>
[Fact]
public void GetFullObjectNameFromQuickInfoWithValidStringsandIgnoreCaseTest()
{
PeekDefinition peekDefinition = new PeekDefinition(null, null);
string objectName = "testtable";
string quickInfoText = "table master.dbo.testTable";
string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName, StringComparison.OrdinalIgnoreCase);
string expected = "master.dbo.testTable"; string expected = "master.dbo.testTable";
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@@ -251,17 +267,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
string objectName = null; string objectName = null;
string quickInfoText = "table master.dbo.testTable"; string quickInfoText = "table master.dbo.testTable";
string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName); string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
Assert.Equal(expected, result); Assert.Equal(expected, result);
quickInfoText = null; quickInfoText = null;
objectName = "tableName"; objectName = "tableName";
result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName); result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
Assert.Equal(expected, result); Assert.Equal(expected, result);
quickInfoText = null; quickInfoText = null;
objectName = null; objectName = null;
result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName); result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@@ -276,7 +292,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
PeekDefinition peekDefinition = new PeekDefinition(null, null); PeekDefinition peekDefinition = new PeekDefinition(null, null);
string objectName = "test"; string objectName = "test";
string quickInfoText = "table master.dbo.tableName"; string quickInfoText = "table master.dbo.tableName";
string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName); string result = peekDefinition.GetFullObjectNameFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
string expected = null; string expected = null;
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@@ -292,7 +308,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
PeekDefinition peekDefinition = new PeekDefinition(null, null); PeekDefinition peekDefinition = new PeekDefinition(null, null);
string objectName = "tableName"; string objectName = "tableName";
string quickInfoText = "table master.dbo.tableName"; string quickInfoText = "table master.dbo.tableName";
string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName); string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
string expected = "table";
Assert.Equal(expected, result);
}
/// <summary>
/// Test extracting the object type from quickInfoText with case insensitive comparison.
/// Given a valid object name string and a vaild quickInfo string containing the object name
/// Expect correct object type
/// </summary>
[Fact]
public void GetTokenTypeFromQuickInfoWithValidStringsandIgnoreCaseTest()
{
PeekDefinition peekDefinition = new PeekDefinition(null, null);
string objectName = "tablename";
string quickInfoText = "table master.dbo.tableName";
string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName, StringComparison.OrdinalIgnoreCase);
string expected = "table"; string expected = "table";
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@@ -310,17 +343,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
string objectName = null; string objectName = null;
string quickInfoText = "table master.dbo.testTable"; string quickInfoText = "table master.dbo.testTable";
string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName); string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
Assert.Equal(expected, result); Assert.Equal(expected, result);
quickInfoText = null; quickInfoText = null;
objectName = "tableName"; objectName = "tableName";
result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName); result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
Assert.Equal(expected, result); Assert.Equal(expected, result);
quickInfoText = null; quickInfoText = null;
objectName = null; objectName = null;
result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName); result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@@ -335,7 +368,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
PeekDefinition peekDefinition = new PeekDefinition(null, null); PeekDefinition peekDefinition = new PeekDefinition(null, null);
string objectName = "test"; string objectName = "test";
string quickInfoText = "table master.dbo.tableName"; string quickInfoText = "table master.dbo.tableName";
string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName); string result = peekDefinition.GetTokenTypeFromQuickInfo(quickInfoText, objectName, StringComparison.Ordinal);
string expected = null; string expected = null;
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }