diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index b39c6849e8..ac8f2d2200 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -53,6 +53,12 @@
Microsoft\Data\Common\ConnectionString\AttestationProtocolUtilities.cs
+
+ Microsoft\Data\Common\ConnectionString\DbConnectionOptions.cs
+
+
+ Microsoft\Data\Common\ConnectionString\DbConnectionOptions.Debug.cs
+
Microsoft\Data\Common\ConnectionString\DbConnectionStringDefaults.cs
@@ -71,9 +77,6 @@
Microsoft\Data\Common\ConnectionString\PoolBlockingUtilities.cs
-
- Microsoft\Data\Common\DbConnectionOptions.Common.cs
-
Microsoft\Data\Common\MultipartIdentifier.cs
@@ -728,7 +731,8 @@
System\Diagnostics\CodeAnalysis.cs
-
+
+
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netcore.cs
similarity index 97%
rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs
rename to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netcore.cs
index 443b7e6734..903af89b3d 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netcore.cs
@@ -5,9 +5,8 @@
using System;
using System.IO;
using System.Text;
-using Microsoft.Data.Common.ConnectionString;
-namespace Microsoft.Data.Common
+namespace Microsoft.Data.Common.ConnectionString
{
internal partial class DbConnectionOptions
{
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
index 52112bfe74..45df50badb 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
@@ -18,6 +18,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
using Microsoft.Data.SqlClient.Diagnostics;
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
index 3a6ef00793..a2ca3dd6c3 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
@@ -7,6 +7,7 @@
using System.Diagnostics;
using System.IO;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs
index c8e782c0b3..133854c1f0 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs
@@ -9,10 +9,10 @@
using System.Threading;
using System.Transactions;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
-
namespace Microsoft.Data.SqlClient
{
public sealed partial class SqlConnection : DbConnection
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
index 6d5753baad..0d1da114cc 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
@@ -9,13 +9,13 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Http.Headers;
-using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
using Microsoft.Identity.Client;
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index d45c126fdb..39ade1bf39 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -237,6 +237,12 @@
Microsoft\Data\Common\ConnectionString\AttestationProtocolUtilities.cs
+
+ Microsoft\Data\Common\ConnectionString\DbConnectionOptions.cs
+
+
+ Microsoft\Data\Common\ConnectionString\DbConnectionOptions.Debug.cs
+
Microsoft\Data\Common\ConnectionString\DbConnectionStringDefaults.cs
@@ -255,9 +261,6 @@
Microsoft\Data\Common\ConnectionString\PoolBlockingUtilities.cs
-
- Microsoft\Data\Common\DbConnectionOptions.Common.cs
-
Microsoft\Data\Common\MultipartIdentifier.cs
@@ -938,7 +941,7 @@
-
+
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netfx.cs
similarity index 92%
rename from src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs
rename to src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netfx.cs
index 80af65c2dc..646682c086 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netfx.cs
@@ -8,7 +8,7 @@
using System.Text;
using Microsoft.Data.Common.ConnectionString;
-namespace Microsoft.Data.Common
+namespace Microsoft.Data.Common.ConnectionString
{
internal partial class DbConnectionOptions
{
@@ -33,20 +33,18 @@ internal bool HasBlankPassword
{
if (!ConvertValueToIntegratedSecurity())
{
- if (_parsetable.ContainsKey(KEY.Password))
+ if (_parsetable.TryGetValue(DbConnectionStringKeywords.Password, out string value))
{
- return string.IsNullOrEmpty(_parsetable[KEY.Password]);
+ return string.IsNullOrEmpty(value);
}
- else
- if (_parsetable.ContainsKey(SYNONYM.Pwd))
+
+ if (_parsetable.TryGetValue(DbConnectionStringSynonyms.Pwd, out value))
{
- return string.IsNullOrEmpty(_parsetable[SYNONYM.Pwd]); // MDAC 83097
- }
- else
- {
- return (_parsetable.ContainsKey(KEY.User_ID) && !string.IsNullOrEmpty(_parsetable[KEY.User_ID])) ||
- (_parsetable.ContainsKey(SYNONYM.UID) && !string.IsNullOrEmpty(_parsetable[SYNONYM.UID]));
+ return string.IsNullOrEmpty(value); // MDAC 83097
}
+
+ return (_parsetable.TryGetValue(DbConnectionStringKeywords.UserID, out value) && !string.IsNullOrEmpty(value)) ||
+ (_parsetable.TryGetValue(DbConnectionStringSynonyms.UID, out value) && !string.IsNullOrEmpty(value));
}
return false;
}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs
index 2be92a6732..04fa51992c 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs
@@ -22,6 +22,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
using Microsoft.SqlServer.Server;
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
index d42a0170aa..bf6f4d759e 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
@@ -3,13 +3,11 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Specialized;
-using System.Configuration;
using System.Data.Common;
using System.Diagnostics;
using System.IO;
-using System.Runtime.Versioning;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
using Microsoft.Data.SqlClient.Server;
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs
index 9f8ddb7930..eb5cb511be 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs
@@ -2,19 +2,20 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
+using System.Data;
+using System.Data.Common;
+using System.Diagnostics;
+using System.Runtime.ConstrainedExecution;
+using System.Threading;
+using System.Transactions;
+using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
+using Microsoft.Data.ProviderBase;
+using Microsoft.Data.SqlClient.ConnectionPool;
+
namespace Microsoft.Data.SqlClient
{
- using System;
- using System.Data;
- using System.Data.Common;
- using System.Diagnostics;
- using System.Runtime.ConstrainedExecution;
- using System.Threading;
- using System.Transactions;
- using Microsoft.Data.Common;
- using Microsoft.Data.ProviderBase;
- using Microsoft.Data.SqlClient.ConnectionPool;
-
public sealed partial class SqlConnection : DbConnection
{
private static readonly DbConnectionFactory _connectionFactory = SqlConnectionFactory.SingletonInstance;
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
index 721f13195f..c5bf4fe9db 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs
@@ -16,6 +16,7 @@
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.ConnectionPool;
using Microsoft.Identity.Client;
@@ -694,7 +695,7 @@ internal override bool IsLockedForBulkCopy
}
}
- internal protected override bool IsNonPoolableTransactionRoot
+ protected internal override bool IsNonPoolableTransactionRoot
{
get
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.Debug.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.Debug.cs
new file mode 100644
index 0000000000..867deb1ac3
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.Debug.cs
@@ -0,0 +1,208 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Text.RegularExpressions;
+using Microsoft.Data.SqlClient;
+
+namespace Microsoft.Data.Common.ConnectionString
+{
+ internal partial class DbConnectionOptions
+ {
+ #if DEBUG
+ private const string ConnectionStringPattern = // may not contain embedded null except trailing last value
+ "([\\s;]*" // leading whitespace and extra semicolons
+ + "(?![\\s;])" // key does not start with space or semicolon
+ + "(?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}]|\\s+==|==)+)" // allow any visible character for keyname except '=' which must quoted as '=='
+ + "\\s*=(?!=)\\s*" // the equal sign divides the key and value parts
+ + "(?"
+ + "(\"([^\"\u0000]|\"\")*\")" // double-quoted string, " must be quoted as ""
+ + "|"
+ + "('([^'\u0000]|'')*')" // single-quoted string, ' must be quoted as ''
+ + "|"
+ + "((?![\"'\\s])" // unquoted value must not start with " or ' or space, would also like = but too late to change
+ + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted
+ + "(?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}])+)" // allow any visible character for keyname except '='
+ + "\\s*=\\s*" // the equal sign divides the key and value parts
+ + "(?"
+ + "(\\{([^\\}\u0000]|\\}\\})*\\})" // quoted string, starts with { and ends with }
+ + "|"
+ + "((?![\\{\\s])" // unquoted value must not start with { or space, would also like = but too late to change
+ + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted
+ + ")" // although the spec does not allow {} embedded within a value, the retail code does.
+ + ")(\\s*)(;|[\u0000\\s]*$)" // whitespace after value up to semicolon or end-of-line
+ + ")*" // repeat the key-value pair
+ + "[\\s;]*[\u0000\\s]*"; // trailing whitespace/semicolons (DataSourceLocator), embedded nulls are allowed only in the end
+ private static readonly Regex ConnectionStringRegexOdbc = new Regex(ConnectionStringPatternOdbc, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
+ #endif
+
+ [Conditional("DEBUG")]
+ private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Dictionary synonyms)
+ {
+ if (SqlClientEventSource.Log.IsAdvancedTraceOn())
+ {
+ Debug.Assert(string.Equals(keyname, keyname?.ToLower(), StringComparison.InvariantCulture), "missing ToLower");
+ string realkeyname = synonyms != null ? synonyms[keyname] : keyname;
+
+ if (!CompareInsensitiveInvariant(DbConnectionStringKeywords.Password, realkeyname) &&
+ !CompareInsensitiveInvariant(DbConnectionStringSynonyms.Pwd, realkeyname))
+ {
+ // don't trace passwords ever!
+ if (keyvalue != null)
+ {
+ SqlClientEventSource.Log.AdvancedTraceEvent(" KeyName='{0}', KeyValue='{1}'", keyname, keyvalue);
+ }
+ else
+ {
+ SqlClientEventSource.Log.AdvancedTraceEvent(" KeyName='{0}'", keyname);
+ }
+ }
+ }
+ }
+
+ #if DEBUG
+ private static void ParseComparison(
+ Dictionary parseTable,
+ string connectionString,
+ Dictionary synonyms,
+ bool firstKey,
+ Exception e)
+ {
+ try
+ {
+ var parsedValues = SplitConnectionString(connectionString, synonyms, firstKey);
+ foreach (var parsedValue in parsedValues)
+ {
+ string key = parsedValue.Key;
+ string value1 = parsedValue.Value;
+
+ bool parseTableContainsKey = parseTable.TryGetValue(key, out string value2);
+ Debug.Assert(parseTableContainsKey, $"{nameof(ParseInternal)} code vs. regex mismatch keyname <{key}>");
+ Debug.Assert(value1 == value2, $"{nameof(ParseInternal)} code vs. regex mismatch keyvalue <{value1}> <{value2}>");
+ }
+ }
+ catch (ArgumentException f)
+ {
+ if (e != null)
+ {
+ string msg1 = e.Message;
+ string msg2 = f.Message;
+
+ const string KeywordNotSupportedMessagePrefix = "Keyword not supported:";
+ const string WrongFormatMessagePrefix = "Format of the initialization string";
+ bool isEquivalent = msg1 == msg2;
+ if (!isEquivalent)
+ {
+ // We also accept cases were Regex parser (debug only) reports "wrong format" and
+ // retail parsing code reports format exception in different location or "keyword not supported"
+ if (msg2.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal))
+ {
+ if (msg1.StartsWith(KeywordNotSupportedMessagePrefix, StringComparison.Ordinal) ||
+ msg1.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal))
+ {
+ isEquivalent = true;
+ }
+ }
+ }
+
+ Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <" + msg1 + "> <" + msg2 + ">");
+ }
+ else
+ {
+ Debug.Fail("ParseInternal code vs regex throw mismatch " + f.Message);
+ }
+
+ e = null;
+ }
+
+ if (e != null)
+ {
+ Debug.Fail("ParseInternal code threw exception vs regex mismatch");
+ }
+ }
+ #endif
+
+ #if DEBUG
+ private static Dictionary SplitConnectionString(
+ string connectionString,
+ Dictionary synonyms,
+ bool firstKey)
+ {
+ var parseTable = new Dictionary();
+ Regex parser = firstKey ? ConnectionStringRegexOdbc : ConnectionStringRegex;
+
+ const int KeyIndex = 1, ValueIndex = 2;
+ Debug.Assert(KeyIndex == parser.GroupNumberFromName("key"), "wrong key index");
+ Debug.Assert(ValueIndex == parser.GroupNumberFromName("value"), "wrong value index");
+
+ if (connectionString != null)
+ {
+ Match match = parser.Match(connectionString);
+ if (!match.Success || match.Length != connectionString.Length)
+ {
+ throw ADP.ConnectionStringSyntax(match.Length);
+ }
+
+ int indexValue = 0;
+ CaptureCollection keyValues = match.Groups[ValueIndex].Captures;
+ foreach (Capture keypair in match.Groups[KeyIndex].Captures)
+ {
+ string keyName = (firstKey ? keypair.Value : keypair.Value.Replace("==", "=")).ToLower(CultureInfo.InvariantCulture);
+ string keyValue = keyValues[indexValue++].Value;
+ if (0 < keyValue.Length)
+ {
+ if (!firstKey)
+ {
+ switch (keyValue[0])
+ {
+ case '\"':
+ keyValue = keyValue.Substring(1, keyValue.Length - 2).Replace("\"\"", "\"");
+ break;
+ case '\'':
+ keyValue = keyValue.Substring(1, keyValue.Length - 2).Replace("\'\'", "\'");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ keyValue = null;
+ }
+
+ DebugTraceKeyValuePair(keyName, keyValue, synonyms);
+ string realKeyName = synonyms != null
+ ? synonyms.TryGetValue(keyName, out string synonym) ? synonym : null
+ : keyName;
+
+ if (!IsKeyNameValid(realKeyName))
+ {
+ throw ADP.KeywordNotSupported(keyName);
+ }
+
+ if (!firstKey || !parseTable.ContainsKey(realKeyName))
+ {
+ parseTable[realKeyName] = keyValue; // last key-value pair wins (or first)
+ }
+ }
+ }
+
+ return parseTable;
+ }
+ #endif
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.cs
similarity index 63%
rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.cs
index f9e83b0120..c8a9461f60 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.cs
@@ -8,87 +8,15 @@
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
-using Microsoft.Data.Common.ConnectionString;
-using Microsoft.Data.SqlClient;
-namespace Microsoft.Data.Common
+namespace Microsoft.Data.Common.ConnectionString
{
- partial class DbConnectionOptions
+ internal partial class DbConnectionOptions
{
// instances of this class are intended to be immutable, i.e readonly
// used by pooling classes so it is much easier to verify correctness
// when not worried about the class being modified during execution
- // connection string common keywords
- private static class KEY
- {
- internal const string Integrated_Security = DbConnectionStringKeywords.IntegratedSecurity;
- internal const string Password = DbConnectionStringKeywords.Password;
- internal const string Persist_Security_Info = DbConnectionStringKeywords.PersistSecurityInfo;
- internal const string User_ID = DbConnectionStringKeywords.UserID;
- internal const string Encrypt = DbConnectionStringKeywords.Encrypt;
- }
-
- // known connection string common synonyms
- private static class SYNONYM
- {
- internal const string Pwd = DbConnectionStringSynonyms.Pwd;
- internal const string UID = DbConnectionStringSynonyms.UID;
- }
-
-#if DEBUG
- /*private const string ConnectionStringPatternV1 =
- "[\\s;]*"
- +"(?([^=\\s]|\\s+[^=\\s]|\\s+==|==)+)"
- + "\\s*=(?!=)\\s*"
- +"(?("
- + "(" + "\"" + "([^\"]|\"\")*" + "\"" + ")"
- + "|"
- + "(" + "'" + "([^']|'')*" + "'" + ")"
- + "|"
- + "(" + "(?![\"'])" + "([^\\s;]|\\s+[^\\s;])*" + "(?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}]|\\s+==|==)+)" // allow any visible character for keyname except '=' which must quoted as '=='
- + "\\s*=(?!=)\\s*" // the equal sign divides the key and value parts
- + "(?"
- + "(\"([^\"\u0000]|\"\")*\")" // double quoted string, " must be quoted as ""
- + "|"
- + "('([^'\u0000]|'')*')" // single quoted string, ' must be quoted as ''
- + "|"
- + "((?![\"'\\s])" // unquoted value must not start with " or ' or space, would also like = but too late to change
- + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted
- + "(?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}])+)" // allow any visible character for keyname except '='
- + "\\s*=\\s*" // the equal sign divides the key and value parts
- + "(?"
- + "(\\{([^\\}\u0000]|\\}\\})*\\})" // quoted string, starts with { and ends with }
- + "|"
- + "((?![\\{\\s])" // unquoted value must not start with { or space, would also like = but too late to change
- + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted
-
- + ")" // although the spec does not allow {}
- // embedded within a value, the retail code does.
- + ")(\\s*)(;|[\u0000\\s]*$)" // whitespace after value up to semicolon or end-of-line
- + ")*" // repeat the key-value pair
- + "[\\s;]*[\u0000\\s]*" // trailing whitespace/semicolons (DataSourceLocator), embedded nulls are allowed only in the end
- ;
-
- private static readonly Regex s_connectionStringRegex = new Regex(ConnectionStringPattern, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
- private static readonly Regex s_connectionStringRegexOdbc = new Regex(ConnectionStringPatternOdbc, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
-#endif
private const string ConnectionStringValidKeyPattern = "^(?![;\\s])[^\\p{Cc}]+(? synonyms)
{
_parsetable = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
- _usersConnectionString = connectionString != null ? connectionString : "";
+ _usersConnectionString = connectionString ?? "";
// first pass on parsing, initial syntax check
- if (0 < _usersConnectionString.Length)
+ if (_usersConnectionString.Length > 0)
{
_keyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, false);
- _hasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd));
- _hasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID));
+ _hasPasswordKeyword = _parsetable.ContainsKey(DbConnectionStringKeywords.Password) ||
+ _parsetable.ContainsKey(DbConnectionStringSynonyms.Pwd);
+ _hasUserIdKeyword = _parsetable.ContainsKey(DbConnectionStringKeywords.UserID) ||
+ _parsetable.ContainsKey(DbConnectionStringSynonyms.UID);
}
}
@@ -139,29 +69,35 @@ protected DbConnectionOptions(DbConnectionOptions connectionOptions)
// same as Boolean, but with SSPI thrown in as valid yes
public bool ConvertValueToIntegratedSecurity()
{
- return _parsetable.TryGetValue(KEY.Integrated_Security, out string value) && value != null ?
- ConvertValueToIntegratedSecurityInternal(value) :
- false;
+ return _parsetable.TryGetValue(DbConnectionStringKeywords.IntegratedSecurity, out string value) && value != null
+ ? ConvertValueToIntegratedSecurityInternal(value)
+ : false;
}
internal bool ConvertValueToIntegratedSecurityInternal(string stringValue)
{
if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes"))
+ {
return true;
- else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no"))
+ }
+
+ if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no"))
+ {
return false;
- else
+ }
+
+ string tmp = stringValue.Trim(); // Remove leading & trailing whitespace.
+ if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes"))
{
- string tmp = stringValue.Trim(); // Remove leading & trailing whitespace.
- if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes"))
- return true;
- else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no"))
- return false;
- else
- {
- throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security);
- }
+ return true;
}
+
+ if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no"))
+ {
+ return false;
+ }
+
+ throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.IntegratedSecurity);
}
public int ConvertValueToInt32(string keyName, int defaultValue)
@@ -208,9 +144,9 @@ private string UsersConnectionString(bool hidePassword, bool forceHidePassword)
return connectionString ?? string.Empty;
}
- internal bool HasPersistablePassword => _hasPasswordKeyword ?
- ConvertValueToBoolean(KEY.Persist_Security_Info, DbConnectionStringDefaults.PersistSecurityInfo) :
- true; // no password means persistable password so we don't have to munge
+ internal bool HasPersistablePassword => _hasPasswordKeyword
+ ? ConvertValueToBoolean(DbConnectionStringKeywords.PersistSecurityInfo, DbConnectionStringDefaults.PersistSecurityInfo)
+ : true; // no password means persistable password so we don't have to munge
public bool ConvertValueToBoolean(string keyName, bool defaultValue)
{
@@ -243,30 +179,6 @@ internal static bool ConvertValueToBooleanInternal(string keyName, string string
private static bool CompareInsensitiveInvariant(string strvalue, string strconst)
=> (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst));
- [System.Diagnostics.Conditional("DEBUG")]
- private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Dictionary synonyms)
- {
- if (SqlClientEventSource.Log.IsAdvancedTraceOn())
- {
- Debug.Assert(string.Equals(keyname, keyname?.ToLower(), StringComparison.InvariantCulture), "missing ToLower");
- string realkeyname = synonyms != null ? synonyms[keyname] : keyname;
-
- if (!string.Equals(KEY.Password, realkeyname, StringComparison.InvariantCultureIgnoreCase) &&
- !string.Equals(SYNONYM.Pwd, realkeyname, StringComparison.InvariantCultureIgnoreCase))
- {
- // don't trace passwords ever!
- if (keyvalue != null)
- {
- SqlClientEventSource.Log.AdvancedTraceEvent(" KeyName='{0}', KeyValue='{1}'", keyname, keyvalue);
- }
- else
- {
- SqlClientEventSource.Log.AdvancedTraceEvent(" KeyName='{0}'", keyname);
- }
- }
- }
- }
-
private static string GetKeyName(StringBuilder buffer)
{
int count = buffer.Length;
@@ -543,130 +455,16 @@ private static bool IsKeyNameValid(string keyname)
return false;
}
-#if DEBUG
- private static Dictionary SplitConnectionString(string connectionString, Dictionary synonyms, bool firstKey)
- {
- var parsetable = new Dictionary();
- Regex parser = (firstKey ? s_connectionStringRegexOdbc : s_connectionStringRegex);
-
- const int KeyIndex = 1, ValueIndex = 2;
- Debug.Assert(KeyIndex == parser.GroupNumberFromName("key"), "wrong key index");
- Debug.Assert(ValueIndex == parser.GroupNumberFromName("value"), "wrong value index");
-
- if (connectionString != null)
- {
- Match match = parser.Match(connectionString);
- if (!match.Success || (match.Length != connectionString.Length))
- {
- throw ADP.ConnectionStringSyntax(match.Length);
- }
- int indexValue = 0;
- CaptureCollection keyvalues = match.Groups[ValueIndex].Captures;
- foreach (Capture keypair in match.Groups[KeyIndex].Captures)
- {
- string keyname = (firstKey ? keypair.Value : keypair.Value.Replace("==", "=")).ToLower(CultureInfo.InvariantCulture);
- string keyvalue = keyvalues[indexValue++].Value;
- if (0 < keyvalue.Length)
- {
- if (!firstKey)
- {
- switch (keyvalue[0])
- {
- case '\"':
- keyvalue = keyvalue.Substring(1, keyvalue.Length - 2).Replace("\"\"", "\"");
- break;
- case '\'':
- keyvalue = keyvalue.Substring(1, keyvalue.Length - 2).Replace("\'\'", "\'");
- break;
- default:
- break;
- }
- }
- }
- else
- {
- keyvalue = null;
- }
- DebugTraceKeyValuePair(keyname, keyvalue, synonyms);
- string synonym;
- string realkeyname = synonyms != null
- ? (synonyms.TryGetValue(keyname, out synonym) ? synonym : null)
- : keyname;
-
- if (!IsKeyNameValid(realkeyname))
- {
- throw ADP.KeywordNotSupported(keyname);
- }
- if (!firstKey || !parsetable.ContainsKey(realkeyname))
- {
- parsetable[realkeyname] = keyvalue; // last key-value pair wins (or first)
- }
- }
- }
- return parsetable;
- }
-
- private static void ParseComparison(Dictionary parsetable, string connectionString, Dictionary synonyms, bool firstKey, Exception e)
- {
- try
- {
- var parsedvalues = SplitConnectionString(connectionString, synonyms, firstKey);
- foreach (var entry in parsedvalues)
- {
- string keyname = entry.Key;
- string value1 = entry.Value;
- string value2;
- bool parsetableContainsKey = parsetable.TryGetValue(keyname, out value2);
- Debug.Assert(parsetableContainsKey, $"{nameof(ParseInternal)} code vs. regex mismatch keyname <{keyname}>");
- Debug.Assert(value1 == value2, $"{nameof(ParseInternal)} code vs. regex mismatch keyvalue <{value1}> <{value2}>");
- }
- }
- catch (ArgumentException f)
- {
- if (e != null)
- {
- string msg1 = e.Message;
- string msg2 = f.Message;
-
- const string KeywordNotSupportedMessagePrefix = "Keyword not supported:";
- const string WrongFormatMessagePrefix = "Format of the initialization string";
- bool isEquivalent = (msg1 == msg2);
- if (!isEquivalent)
- {
- // We also accept cases were Regex parser (debug only) reports "wrong format" and
- // retail parsing code reports format exception in different location or "keyword not supported"
- if (msg2.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal))
- {
- if (msg1.StartsWith(KeywordNotSupportedMessagePrefix, StringComparison.Ordinal) || msg1.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal))
- {
- isEquivalent = true;
- }
- }
- }
- Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <" + msg1 + "> <" + msg2 + ">");
- }
- else
- {
- Debug.Fail("ParseInternal code vs regex throw mismatch " + f.Message);
- }
- e = null;
- }
- if (e != null)
- {
- Debug.Fail("ParseInternal code threw exception vs regex mismatch");
- }
- }
-#endif
-
private static NameValuePair ParseInternal(Dictionary parsetable, string connectionString, bool buildChain, Dictionary synonyms, bool firstKey)
{
Debug.Assert(connectionString != null, "null connectionstring");
StringBuilder buffer = new StringBuilder();
NameValuePair localKeychain = null, keychain = null;
-#if DEBUG
+
+ #if DEBUG
try
{
-#endif
+ #endif
int nextStartPosition = 0;
int endPosition = connectionString.Length;
while (nextStartPosition < endPosition)
@@ -680,9 +478,8 @@ private static NameValuePair ParseInternal(Dictionary parsetable
// if (nextStartPosition != endPosition) { throw; }
break;
}
-#if DEBUG
+
DebugTraceKeyValuePair(keyname, keyvalue, synonyms);
-#endif
Debug.Assert(IsKeyNameValid(keyname), "ParseFailure, invalid keyname");
Debug.Assert(IsValueValidInternal(keyvalue), "parse failure, invalid keyvalue");
@@ -708,7 +505,7 @@ private static NameValuePair ParseInternal(Dictionary parsetable
keychain = localKeychain = new NameValuePair(realkeyname, keyvalue, nextStartPosition - startPosition);
}
}
-#if DEBUG
+ #if DEBUG
}
catch (ArgumentException e)
{
@@ -716,7 +513,8 @@ private static NameValuePair ParseInternal(Dictionary parsetable
throw;
}
ParseComparison(parsetable, connectionString, synonyms, firstKey, null);
-#endif
+ #endif
+
return keychain;
}
@@ -728,8 +526,8 @@ internal NameValuePair ReplacePasswordPwd(out string constr, bool fakePassword)
StringBuilder builder = new StringBuilder(_usersConnectionString.Length);
for (NameValuePair current = _keyChain; current != null; current = current.Next)
{
- if (!string.Equals(KEY.Password, current.Name, StringComparison.InvariantCultureIgnoreCase) &&
- !string.Equals(SYNONYM.Pwd, current.Name, StringComparison.InvariantCultureIgnoreCase))
+ if (!CompareInsensitiveInvariant(DbConnectionStringKeywords.Password, current.Name) &&
+ !CompareInsensitiveInvariant(DbConnectionStringSynonyms.Pwd, current.Name))
{
builder.Append(_usersConnectionString, copyPosition, current.Length);
if (fakePassword)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionClosed.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionClosed.cs
index b8ada9b1bc..d4ea183312 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionClosed.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionClosed.cs
@@ -2,12 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Microsoft.Data.Common;
-using Microsoft.Data.SqlClient.ConnectionPool;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Threading.Tasks;
+using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
+using Microsoft.Data.SqlClient.ConnectionPool;
namespace Microsoft.Data.ProviderBase
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs
index 6f12c748f2..832b1dc2dc 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs
@@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlClient.ConnectionPool;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
index 53ff96963c..b4dfb4f214 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
@@ -10,6 +10,7 @@
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlClient.ConnectionPool;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs
index 110f578a85..805beed2b5 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs
@@ -2,14 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-
-using Microsoft.Data.Common;
-using Microsoft.Data.ProviderBase;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
+using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
+using Microsoft.Data.ProviderBase;
namespace Microsoft.Data.SqlClient.ConnectionPool
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs
index e0bbcb8f24..d6647f832e 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs
@@ -7,7 +7,7 @@
using System.Data.Common;
using System.Threading.Tasks;
using System.Transactions;
-using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
namespace Microsoft.Data.SqlClient.ConnectionPool
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs
index 162b533ecc..77d2b3bae1 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs
@@ -14,6 +14,7 @@
using System.Threading.Tasks;
using System.Transactions;
using Microsoft.Data.Common;
+using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.ProviderBase;
using static Microsoft.Data.SqlClient.ConnectionPool.DbConnectionPoolState;