Skip to content

Commit

Permalink
SNOW-1230345 Fix to support quoted values in object connection proper…
Browse files Browse the repository at this point in the history
…ties (#891)

### Description
 Fix to support quoted values in object connection properties.
Supported object properties with scaped quotes:
- DB
- SCHEMA
- ROLE
- WAREHOUSE

### Checklist
- [x] Code compiles correctly
- [x] Code is formatted according to [Coding
Conventions](../CodingConventions.md)
- [x] Created tests which fail without the change (if possible)
- [x] All tests passing (`dotnet test`)
- [x] Extended the README / documentation, if necessary
- [x] Provide JIRA issue id (if possible) or GitHub issue id in PR name
  • Loading branch information
sfc-gh-jmartinez committed Mar 20, 2024
1 parent a25f8c5 commit 8fa5df5
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 45 deletions.
4 changes: 2 additions & 2 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1652,7 +1652,7 @@ public void testMulitpleConnectionInParallel()
}

[Test]
[Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::parseConnectionString() to verify")]
[Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::ParseConnectionString() to verify")]
public void TestEscapeChar()
{
using (IDbConnection conn = new SnowflakeDbConnection())
Expand All @@ -1679,7 +1679,7 @@ public void TestEscapeChar()
}

[Test]
[Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::parseConnectionString() to verify")]
[Ignore("Ignore this test, please test this manual with breakpoint at SFSessionProperty::ParseConnectionString() to verify")]
public void TestEscapeChar1()
{
using (IDbConnection conn = new SnowflakeDbConnection())
Expand Down
43 changes: 39 additions & 4 deletions Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@

namespace Snowflake.Data.Tests.UnitTests
{

class SFSessionPropertyTest
{

[Test, TestCaseSource(nameof(ConnectionStringTestCases))]
public void TestThatPropertiesAreParsed(TestCase testcase)
{
// act
var properties = SFSessionProperties.parseConnectionString(
var properties = SFSessionProperties.ParseConnectionString(
testcase.ConnectionString,
testcase.SecurePassword);

Expand All @@ -40,7 +41,7 @@ public void TestValidateCorrectAccountNames(string accountName, string expectedA
var connectionString = $"ACCOUNT={accountName};USER=test;PASSWORD=test;";

// act
var properties = SFSessionProperties.parseConnectionString(connectionString, null);
var properties = SFSessionProperties.ParseConnectionString(connectionString, null);

// assert
Assert.AreEqual(expectedAccountName, properties[SFSessionProperty.ACCOUNT]);
Expand All @@ -60,7 +61,7 @@ public void TestThatItFailsForWrongConnectionParameter(string connectionString,
{
// act
var exception = Assert.Throws<SnowflakeDbException>(
() => SFSessionProperties.parseConnectionString(connectionString, null)
() => SFSessionProperties.ParseConnectionString(connectionString, null)
);

// assert
Expand All @@ -75,13 +76,46 @@ public void TestThatItFailsIfNoAccountSpecified(string connectionString)
{
// act
var exception = Assert.Throws<SnowflakeDbException>(
() => SFSessionProperties.parseConnectionString(connectionString, null)
() => SFSessionProperties.ParseConnectionString(connectionString, null)
);

// assert
Assert.AreEqual(SFError.MISSING_CONNECTION_PROPERTY.GetAttribute<SFErrorAttr>().errorCode, exception.ErrorCode);
}



[Test]
[TestCase("DB", SFSessionProperty.DB, "\"testdb\"")]
[TestCase("SCHEMA", SFSessionProperty.SCHEMA, "\"quotedSchema\"")]
[TestCase("ROLE", SFSessionProperty.ROLE, "\"userrole\"")]
[TestCase("WAREHOUSE", SFSessionProperty.WAREHOUSE, "\"warehouse test\"")]
public void TestValidateSupportEscapedQuotesValuesForObjectProperties(string propertyName, SFSessionProperty sessionProperty, string value)
{
// arrange
var connectionString = $"ACCOUNT=test;{propertyName}={value};USER=test;PASSWORD=test;";

// act
var properties = SFSessionProperties.ParseConnectionString(connectionString, null);

// assert
Assert.AreEqual(value, properties[sessionProperty]);
}

[Test]
public void TestProcessEmptyUserAndPasswordInConnectionString()
{
// arrange
var connectionString = $"ACCOUNT=test;USER=;PASSWORD=;";

// act
var properties = SFSessionProperties.ParseConnectionString(connectionString, null);

// assert
Assert.AreEqual(string.Empty, properties[SFSessionProperty.USER]);
Assert.AreEqual(string.Empty, properties[SFSessionProperty.PASSWORD]);
}

public static IEnumerable<TestCase> ConnectionStringTestCases()
{
string defAccount = "testaccount";
Expand Down Expand Up @@ -134,6 +168,7 @@ public static IEnumerable<TestCase> ConnectionStringTestCases()
{ SFSessionProperty.ALLOWUNDERSCORESINHOST, defAllowUnderscoresInHost }
}
};

var testCaseWithBrowserResponseTimeout = new TestCase()
{
ConnectionString = $"ACCOUNT={defAccount};BROWSER_RESPONSE_TIMEOUT=180;authenticator=externalbrowser",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void TestExtractProperties(PropertiesTestCase testCase)
// arrange
var proxyExtractorMock = new Moq.Mock<SFSessionHttpClientProxyProperties.IExtractor>();
var extractor = new SFSessionHttpClientProperties.Extractor(proxyExtractorMock.Object);
var properties = SFSessionProperties.parseConnectionString(testCase.conectionString, null);
var properties = SFSessionProperties.ParseConnectionString(testCase.conectionString, null);
var proxyProperties = new SFSessionHttpClientProxyProperties();
proxyExtractorMock
.Setup(e => e.ExtractProperties(properties))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void ShouldExtractProxyProperties(ProxyPropertiesTestCase testCase)
{
// given
var extractor = new SFSessionHttpClientProxyProperties.Extractor();
var properties = SFSessionProperties.parseConnectionString(testCase.conectionString, null);
var properties = SFSessionProperties.ParseConnectionString(testCase.conectionString, null);

// when
var proxyProperties = extractor.ExtractProperties(properties);
Expand Down
2 changes: 1 addition & 1 deletion Snowflake.Data/Core/Session/SFSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ internal Uri BuildLoginUrl()
{
_easyLoggingStarter = easyLoggingStarter;
connStr = connectionString;
properties = SFSessionProperties.parseConnectionString(connectionString, password);
properties = SFSessionProperties.ParseConnectionString(connectionString, password);
_disableQueryContextCache = bool.Parse(properties[SFSessionProperty.DISABLEQUERYCONTEXTCACHE]);
_disableConsoleLogin = bool.Parse(properties[SFSessionProperty.DISABLE_CONSOLE_LOGIN]);
ValidateApplicationName(properties);
Expand Down
88 changes: 52 additions & 36 deletions Snowflake.Data/Core/Session/SFSessionProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,10 @@ public override int GetHashCode()
return base.GetHashCode();
}

internal static SFSessionProperties parseConnectionString(String connectionString, SecureString password)
internal static SFSessionProperties ParseConnectionString(string connectionString, SecureString password)
{
logger.Info("Start parsing connection string.");
DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
var builder = new DbConnectionStringBuilder();
try
{
builder.ConnectionString = connectionString;
Expand All @@ -175,14 +175,14 @@ internal static SFSessionProperties parseConnectionString(String connectionStrin
SFError.INVALID_CONNECTION_STRING,
e.Message);
}
SFSessionProperties properties = new SFSessionProperties();
var properties = new SFSessionProperties();

string[] keys = new string[builder.Keys.Count];
string[] values = new string[builder.Values.Count];
var keys = new string[builder.Keys.Count];
var values = new string[builder.Values.Count];
builder.Keys.CopyTo(keys, 0);
builder.Values.CopyTo(values,0);

for(int i=0; i<keys.Length; i++)
for(var i=0; i<keys.Length; i++)
{
try
{
Expand All @@ -195,37 +195,10 @@ internal static SFSessionProperties parseConnectionString(String connectionStrin
logger.Warn($"Property {keys[i]} not found ignored.", e);
}
}

UpdatePropertiesForSpecialCases(properties, connectionString);

//handle DbConnectionStringBuilder missing cases
string[] propertyEntry = connectionString.Split(';');
foreach(string keyVal in propertyEntry)
{
if(keyVal.Length > 0)
{
string[] tokens = keyVal.Split(new string[] { "=" }, StringSplitOptions.None);
if(tokens[0].ToUpper() == "DB" || tokens[0].ToUpper() == "SCHEMA" ||
tokens[0].ToLower() == "WAREHOUSE" || tokens[0].ToUpper() == "ROLE")
{
if (tokens.Length == 2)
{
SFSessionProperty p = (SFSessionProperty)Enum.Parse(
typeof(SFSessionProperty), tokens[0].ToUpper());
properties[p]= tokens[1];
}
}
if(tokens[0].ToUpper() == "USER" || tokens[0].ToUpper() == "PASSWORD")
{
SFSessionProperty p = (SFSessionProperty)Enum.Parse(
typeof(SFSessionProperty), tokens[0].ToUpper());
if (!properties.ContainsKey(p))
{
properties.Add(p, "");
}
}
}
}

bool useProxy = false;
var useProxy = false;
if (properties.ContainsKey(SFSessionProperty.USEPROXY))
{
try
Expand Down Expand Up @@ -292,6 +265,49 @@ internal static SFSessionProperties parseConnectionString(String connectionStrin
return properties;
}

private static void UpdatePropertiesForSpecialCases(SFSessionProperties properties, string connectionString)
{
var propertyEntry = connectionString.Split(';');
foreach(var keyVal in propertyEntry)
{
if(keyVal.Length > 0)
{
var tokens = keyVal.Split(new string[] { "=" }, StringSplitOptions.None);
var propertyName = tokens[0].ToUpper();
switch (propertyName)
{
case "DB":
case "SCHEMA":
case "WAREHOUSE":
case "ROLE":
{
if (tokens.Length == 2)
{
var sessionProperty = (SFSessionProperty)Enum.Parse(
typeof(SFSessionProperty), propertyName);
properties[sessionProperty]= tokens[1];
}

break;
}
case "USER":
case "PASSWORD":
{

var sessionProperty = (SFSessionProperty)Enum.Parse(
typeof(SFSessionProperty), propertyName);
if (!properties.ContainsKey(sessionProperty))
{
properties.Add(sessionProperty, "");
}

break;
}
}
}
}
}

private static void ValidateAccountDomain(SFSessionProperties properties)
{
var account = properties[SFSessionProperty.ACCOUNT];
Expand Down

0 comments on commit 8fa5df5

Please sign in to comment.