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 e46d123395..5a299ba2e1 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -51,6 +51,12 @@
Microsoft\Data\Common\ActivityCorrelator.cs
+
+ Microsoft\Data\Common\DbConnectionPoolKey.cs
+
+
+ Microsoft\Data\Common\MultipartIdentifier.cs
+
Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs
@@ -321,9 +327,6 @@
Microsoft\Data\Common\DbConnectionOptions.Common.cs
-
- Microsoft\Data\Common\DbConnectionPoolKey.cs
-
Microsoft\Data\Common\FieldNameLookup.cs
@@ -331,9 +334,6 @@
Microsoft\Data\Common\BasicFieldNameLookup.cs
-
- Microsoft\Data\Common\MultipartIdentifier.cs
-
Microsoft\Data\Common\NameValuePair.cs
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/DataStorage.cs b/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/DataStorage.cs
deleted file mode 100644
index 713ddcffb6..0000000000
--- a/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/DataStorage.cs
+++ /dev/null
@@ -1,531 +0,0 @@
-// 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.
-
-namespace Microsoft.Data.Common {
- using System;
- using System.Collections;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using Microsoft.Data.SqlTypes;
- using System.Diagnostics;
- using System.Xml;
- using System.Xml.Serialization;
- using System.Data.SqlTypes;
- using System.Data;
-
- internal enum StorageType {
- Empty = TypeCode.Empty, // 0
- Object = TypeCode.Object,
- DBNull = TypeCode.DBNull,
- Boolean = TypeCode.Boolean,
- Char = TypeCode.Char,
- SByte = TypeCode.SByte,
- Byte = TypeCode.Byte,
- Int16 = TypeCode.Int16,
- UInt16 = TypeCode.UInt16,
- Int32 = TypeCode.Int32,
- UInt32 = TypeCode.UInt32,
- Int64 = TypeCode.Int64,
- UInt64 = TypeCode.UInt64,
- Single = TypeCode.Single,
- Double = TypeCode.Double,
- Decimal = TypeCode.Decimal, // 15
- DateTime = TypeCode.DateTime, // 16
- TimeSpan = 17,
- String = TypeCode.String, // 18
- Guid = 19,
-
- ByteArray = 20,
- CharArray = 21,
- Type = 22,
- DateTimeOffset = 23,
- BigInteger = 24,
- Uri = 25,
-
- SqlBinary, // SqlTypes should remain at the end for IsSqlType checking
- SqlBoolean,
- SqlByte,
- SqlBytes,
- SqlChars,
- SqlDateTime,
- SqlDecimal,
- SqlDouble,
- SqlGuid,
- SqlInt16,
- SqlInt32,
- SqlInt64,
- SqlMoney,
- SqlSingle,
- SqlString,
-// SqlXml,
- };
-
- abstract internal class DataStorage {
-
- // for Whidbey 40426, searching down the Type[] is about 20% faster than using a Dictionary
- // must keep in same order as enum StorageType
- private static readonly Type[] StorageClassType = new Type[] {
- null,
- typeof(Object),
- typeof(DBNull),
- typeof(Boolean),
- typeof(Char),
- typeof(SByte),
- typeof(Byte),
- typeof(Int16),
- typeof(UInt16),
- typeof(Int32),
- typeof(UInt32),
- typeof(Int64),
- typeof(UInt64),
- typeof(Single),
- typeof(Double),
- typeof(Decimal),
- typeof(DateTime),
- typeof(TimeSpan),
- typeof(String),
- typeof(Guid),
-
- typeof(byte[]),
- typeof(char[]),
- typeof(Type),
- typeof(DateTimeOffset),
- typeof(System.Numerics.BigInteger),
- typeof(Uri),
-
- typeof(SqlBinary),
- typeof(SqlBoolean),
- typeof(SqlByte),
- typeof(SqlBytes),
- typeof(SqlChars),
- typeof(SqlDateTime),
- typeof(SqlDecimal),
- typeof(SqlDouble),
- typeof(SqlGuid),
- typeof(SqlInt16),
- typeof(SqlInt32),
- typeof(SqlInt64),
- typeof(SqlMoney),
- typeof(SqlSingle),
- typeof(SqlString),
-// typeof(SqlXml),
- };
-
- internal readonly DataColumn Column;
- internal readonly DataTable Table;
- internal readonly Type DataType;
- internal readonly StorageType StorageTypeCode;
- private System.Collections.BitArray dbNullBits;
-
- private readonly object DefaultValue;
- internal readonly object NullValue;
-
- internal readonly bool IsCloneable;
- internal readonly bool IsCustomDefinedType;
- internal readonly bool IsStringType;
- internal readonly bool IsValueType;
-
- private readonly static Func> _inspectTypeForInterfaces = InspectTypeForInterfaces;
- private readonly static ConcurrentDictionary> _typeImplementsInterface = new ConcurrentDictionary>();
-
- protected DataStorage(DataColumn column, Type type, object defaultValue, StorageType storageType)
- : this(column, type, defaultValue, DBNull.Value, false, storageType) {
- }
-
- protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, StorageType storageType)
- : this(column, type, defaultValue, nullValue, false, storageType) {
- }
-
- protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, bool isICloneable, StorageType storageType) {
- Debug.Assert(storageType == GetStorageType(type), "Incorrect storage type specified");
- Column = column;
- Table = column.Table;
- DataType = type;
- StorageTypeCode = storageType;
- DefaultValue = defaultValue;
- NullValue = nullValue;
- IsCloneable = isICloneable;
- IsCustomDefinedType = IsTypeCustomType(StorageTypeCode);
- IsStringType = ((StorageType.String == StorageTypeCode) || (StorageType.SqlString == StorageTypeCode));
- IsValueType = DetermineIfValueType(StorageTypeCode, type);
- }
-
- internal DataSetDateTime DateTimeMode {
- get {
- return Column.DateTimeMode;
- }
- }
-
- internal IFormatProvider FormatProvider {
- get {
- return Table.FormatProvider;
- }
- }
-
- public virtual Object Aggregate(int[] recordNos, AggregateType kind) {
- if (AggregateType.Count == kind) {
- return this.AggregateCount(recordNos);
- }
- return null;
- }
-
- public object AggregateCount(int[] recordNos) {
- int count = 0;
- for (int i = 0; i < recordNos.Length; i++) {
- if (!this.dbNullBits.Get(recordNos[i]))
- count++;
- }
- return count;
- }
-
- protected int CompareBits(int recordNo1, int recordNo2) {
- bool recordNo1Null = this.dbNullBits.Get(recordNo1);
- bool recordNo2Null = this.dbNullBits.Get(recordNo2);
- if (recordNo1Null ^ recordNo2Null) {
- if (recordNo1Null)
- return -1;
- else
- return 1;
- }
-
- return 0;
- }
-
- public abstract int Compare(int recordNo1, int recordNo2);
-
- // only does comparision, expect value to be of the correct type
- public abstract int CompareValueTo(int recordNo1, object value);
-
- // only does conversion with support for reference null
- public virtual object ConvertValue(object value) {
- return value;
- }
-
- protected void CopyBits(int srcRecordNo, int dstRecordNo) {
- this.dbNullBits.Set(dstRecordNo, this.dbNullBits.Get(srcRecordNo));
- }
-
- abstract public void Copy(int recordNo1, int recordNo2);
-
- abstract public Object Get(int recordNo);
-
- protected Object GetBits(int recordNo) {
- if (this.dbNullBits.Get(recordNo)) {
- return NullValue;
- }
- return DefaultValue;
- }
-
- virtual public int GetStringLength(int record) {
- System.Diagnostics.Debug.Assert(false, "not a String or SqlString column");
- return Int32.MaxValue;
- }
-
- protected bool HasValue(int recordNo) {
- return !this.dbNullBits.Get(recordNo);
- }
-
- public virtual bool IsNull(int recordNo) {
- return this.dbNullBits.Get(recordNo);
- }
-
- // convert (may not support reference null) and store the value
- abstract public void Set(int recordNo, Object value);
-
- protected void SetNullBit(int recordNo, bool flag) {
- this.dbNullBits.Set(recordNo, flag);
- }
-
- virtual public void SetCapacity(int capacity) {
- if (null == this.dbNullBits) {
- this.dbNullBits = new BitArray(capacity);
- }
- else {
- this.dbNullBits.Length = capacity;
- }
- }
-
- abstract public object ConvertXmlToObject(string s);
- public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) {
- return ConvertXmlToObject(xmlReader.Value);
- }
-
- abstract public string ConvertObjectToXml(object value);
- public virtual void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) {
- xmlWriter.WriteString(ConvertObjectToXml(value));// should it be NO OP?
- }
-
- public static DataStorage CreateStorage(DataColumn column, Type dataType, StorageType typeCode) {
- Debug.Assert(typeCode == GetStorageType(dataType), "Incorrect storage type specified");
- if ((StorageType.Empty == typeCode) && (null != dataType)) {
- if (typeof(INullable).IsAssignableFrom(dataType)) { // Udt, OracleTypes
- return new SqlUdtStorage(column, dataType);
- }
- else {
- return new ObjectStorage(column, dataType); // non-nullable non-primitives
- }
- }
-
- switch (typeCode) {
- case StorageType.Empty: throw ExceptionBuilder.InvalidStorageType(TypeCode.Empty);
- case StorageType.DBNull: throw ExceptionBuilder.InvalidStorageType(TypeCode.DBNull);
- case StorageType.Object: return new ObjectStorage(column, dataType);
- case StorageType.Boolean: return new BooleanStorage(column);
- case StorageType.Char: return new CharStorage(column);
- case StorageType.SByte: return new SByteStorage(column);
- case StorageType.Byte: return new ByteStorage(column);
- case StorageType.Int16: return new Int16Storage(column);
- case StorageType.UInt16: return new UInt16Storage(column);
- case StorageType.Int32: return new Int32Storage(column);
- case StorageType.UInt32: return new UInt32Storage(column);
- case StorageType.Int64: return new Int64Storage(column);
- case StorageType.UInt64: return new UInt64Storage(column);
- case StorageType.Single: return new SingleStorage(column);
- case StorageType.Double: return new DoubleStorage(column);
- case StorageType.Decimal: return new DecimalStorage(column);
- case StorageType.DateTime: return new DateTimeStorage(column);
- case StorageType.TimeSpan: return new TimeSpanStorage(column);
- case StorageType.String: return new StringStorage(column);
- case StorageType.Guid: return new ObjectStorage(column, dataType);
-
- case StorageType.ByteArray: return new ObjectStorage(column, dataType);
- case StorageType.CharArray: return new ObjectStorage(column, dataType);
- case StorageType.Type: return new ObjectStorage(column, dataType);
- case StorageType.DateTimeOffset: return new DateTimeOffsetStorage(column);
- case StorageType.BigInteger: return new BigIntegerStorage(column);
- case StorageType.Uri: return new ObjectStorage(column, dataType);
-
- case StorageType.SqlBinary: return new SqlBinaryStorage(column);
- case StorageType.SqlBoolean: return new SqlBooleanStorage(column);
- case StorageType.SqlByte: return new SqlByteStorage(column);
- case StorageType.SqlBytes: return new SqlBytesStorage(column);
- case StorageType.SqlChars: return new SqlCharsStorage(column);
- case StorageType.SqlDateTime: return new SqlDateTimeStorage(column); //???/ what to do
- case StorageType.SqlDecimal: return new SqlDecimalStorage(column);
- case StorageType.SqlDouble: return new SqlDoubleStorage(column);
- case StorageType.SqlGuid: return new SqlGuidStorage(column);
- case StorageType.SqlInt16: return new SqlInt16Storage(column);
- case StorageType.SqlInt32: return new SqlInt32Storage(column);
- case StorageType.SqlInt64: return new SqlInt64Storage(column);
- case StorageType.SqlMoney: return new SqlMoneyStorage(column);
- case StorageType.SqlSingle: return new SqlSingleStorage(column);
- case StorageType.SqlString: return new SqlStringStorage(column);
- // case StorageType.SqlXml: return new SqlXmlStorage(column);
-
- default:
- System.Diagnostics.Debug.Assert(false, "shouldn't be here");
- goto case StorageType.Object;
- }
- }
-
- internal static StorageType GetStorageType(Type dataType) {
- for (int i = 0; i < StorageClassType.Length; ++i) {
- if (dataType == StorageClassType[i]) {
- return (StorageType)i;
- }
- }
- TypeCode tcode = Type.GetTypeCode(dataType);
- if (TypeCode.Object != tcode) { // enum -> Int64/Int32/Int16/Byte
- return (StorageType)tcode;
- }
- return StorageType.Empty;
- }
-
- internal static Type GetTypeStorage(StorageType storageType) {
- return StorageClassType[(int)storageType];
- }
-
- internal static bool IsTypeCustomType(Type type) {
- return IsTypeCustomType(GetStorageType(type));
- }
-
- internal static bool IsTypeCustomType(StorageType typeCode) {
- return ((StorageType.Object == typeCode) || (StorageType.Empty == typeCode) || (StorageType.CharArray == typeCode));
- }
-
- internal static bool IsSqlType(StorageType storageType) {
- return (StorageType.SqlBinary <= storageType);
- }
-
- public static bool IsSqlType(Type dataType) {
- for (int i = (int)StorageType.SqlBinary; i < StorageClassType.Length; ++i) {
- if (dataType == StorageClassType[i]) {
- return true;
- }
- }
- return false;
- }
-
- private static bool DetermineIfValueType(StorageType typeCode, Type dataType) {
- bool result;
- switch (typeCode) {
- case StorageType.Boolean:
- case StorageType.Char:
- case StorageType.SByte:
- case StorageType.Byte:
- case StorageType.Int16:
- case StorageType.UInt16:
- case StorageType.Int32:
- case StorageType.UInt32:
- case StorageType.Int64:
- case StorageType.UInt64:
- case StorageType.Single:
- case StorageType.Double:
- case StorageType.Decimal:
- case StorageType.DateTime:
- case StorageType.DateTimeOffset:
- case StorageType.BigInteger:
- case StorageType.TimeSpan:
- case StorageType.Guid:
- case StorageType.SqlBinary:
- case StorageType.SqlBoolean:
- case StorageType.SqlByte:
- case StorageType.SqlDateTime:
- case StorageType.SqlDecimal:
- case StorageType.SqlDouble:
- case StorageType.SqlGuid:
- case StorageType.SqlInt16:
- case StorageType.SqlInt32:
- case StorageType.SqlInt64:
- case StorageType.SqlMoney:
- case StorageType.SqlSingle:
- case StorageType.SqlString:
- result = true;
- break;
-
- case StorageType.String:
- case StorageType.ByteArray:
- case StorageType.CharArray:
- case StorageType.Type:
- case StorageType.Uri:
- case StorageType.SqlBytes:
- case StorageType.SqlChars:
- result = false;
- break;
-
- default:
- result = dataType.IsValueType;
- break;
- }
- Debug.Assert(result == dataType.IsValueType, "typeCode mismatches dataType");
- return result;
- }
-
- internal static void ImplementsInterfaces(
- StorageType typeCode,
- Type dataType,
- out bool sqlType,
- out bool nullable,
- out bool xmlSerializable,
- out bool changeTracking,
- out bool revertibleChangeTracking)
- {
- Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType");
- if (IsSqlType(typeCode)) {
- sqlType = true;
- nullable = true;
- changeTracking = false;
- revertibleChangeTracking = false;
- xmlSerializable = true;
- }
- else if (StorageType.Empty != typeCode) {
- sqlType = false;
- nullable = false;
- changeTracking = false;
- revertibleChangeTracking = false;
- xmlSerializable = false;
- }
- else {
- // Non-standard type - look it up in the dictionary or add it if not found
- Tuple interfaces = _typeImplementsInterface.GetOrAdd(dataType, _inspectTypeForInterfaces);
- sqlType = false;
- nullable = interfaces.Item1;
- changeTracking = interfaces.Item2;
- revertibleChangeTracking = interfaces.Item3;
- xmlSerializable = interfaces.Item4;
- }
- Debug.Assert(nullable == typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(dataType), "INullable");
- Debug.Assert(changeTracking == typeof(System.ComponentModel.IChangeTracking).IsAssignableFrom(dataType), "IChangeTracking");
- Debug.Assert(revertibleChangeTracking == typeof(System.ComponentModel.IRevertibleChangeTracking).IsAssignableFrom(dataType), "IRevertibleChangeTracking");
- Debug.Assert(xmlSerializable == typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(dataType), "IXmlSerializable");
- }
-
- private static Tuple InspectTypeForInterfaces(Type dataType) {
- Debug.Assert(dataType != null, "Type should not be null");
-
- return new Tuple(
- typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(dataType),
- typeof(System.ComponentModel.IChangeTracking).IsAssignableFrom(dataType),
- typeof(System.ComponentModel.IRevertibleChangeTracking).IsAssignableFrom(dataType),
- typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(dataType));
- }
-
- internal static bool ImplementsINullableValue(StorageType typeCode, Type dataType) {
- Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType");
- return ((StorageType.Empty == typeCode) && dataType.IsGenericType && (dataType.GetGenericTypeDefinition() == typeof(System.Nullable<>)));
- }
-
- public static bool IsObjectNull(object value) {
- return ((null == value) || (DBNull.Value == value) || IsObjectSqlNull(value));
- }
-
- public static bool IsObjectSqlNull(object value) {
- INullable inullable = (value as INullable);
- return ((null != inullable) && inullable.IsNull);
- }
-
- internal object GetEmptyStorageInternal(int recordCount) {
- return GetEmptyStorage(recordCount);
- }
-
- internal void CopyValueInternal(int record, object store, BitArray nullbits, int storeIndex) {
- CopyValue(record, store, nullbits, storeIndex);
- }
-
- internal void SetStorageInternal(object store, BitArray nullbits) {
- SetStorage(store, nullbits);
- }
-
- abstract protected Object GetEmptyStorage(int recordCount);
- abstract protected void CopyValue(int record, object store, BitArray nullbits, int storeIndex);
- abstract protected void SetStorage(object store, BitArray nullbits);
- protected void SetNullStorage(BitArray nullbits) {
- dbNullBits = nullbits;
- }
-
- /// wrapper around Type.GetType
- /// assembly qualified type name or one of the special known types
- /// Type or null if not found
- /// when type implements IDynamicMetaObjectProvider and not IXmlSerializable
- ///
- /// Types like "System.Guid" will load regardless of AssemblyQualifiedName because they are special
- /// Types like "System.Data.SqlTypes.SqlString" will load because they are in the same assembly as this code
- /// Types like "System.Numerics.BigInteger" won't load because they are not special and not same assembly as this code
- ///
- internal static Type GetType(string value) {
- Type dataType = Type.GetType(value); // throwOnError=false, ignoreCase=fase
- if (null == dataType) {
- if ("System.Numerics.BigInteger" == value) {
- dataType = typeof(System.Numerics.BigInteger);
- }
- }
-
- // Dev10 671061: prevent reading type from schema which implements IDynamicMetaObjectProvider and not IXmlSerializable
- // the check here prevents the type from being loaded in schema or as instance data (when DataType is object)
- ObjectStorage.VerifyIDynamicMetaObjectProvider(dataType);
- return dataType;
- }
-
- /// wrapper around Type.AssemblyQualifiedName
- ///
- /// qualified name when writing in xml
- /// when type implements IDynamicMetaObjectProvider and not IXmlSerializable
- internal static string GetQualifiedName(Type type)
- {
- Debug.Assert(null != type, "null type");
- ObjectStorage.VerifyIDynamicMetaObjectProvider(type);
- return type.AssemblyQualifiedName;
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/DbConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/DbConnectionPoolKey.cs
deleted file mode 100644
index 0334016847..0000000000
--- a/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/DbConnectionPoolKey.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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.
-
-namespace Microsoft.Data.Common
-{
-
- using System;
-
- // DbConnectionPoolKey: Base class implementation of a key to connection pool groups
- // Only connection string is used as a key
- internal class DbConnectionPoolKey : ICloneable
- {
- private string _connectionString;
-
- internal DbConnectionPoolKey(string connectionString)
- {
- _connectionString = connectionString;
- }
-
- protected DbConnectionPoolKey(DbConnectionPoolKey key)
- {
- _connectionString = key.ConnectionString;
- }
-
- object ICloneable.Clone()
- {
- return new DbConnectionPoolKey(this);
- }
-
- internal virtual string ConnectionString
- {
- get
- {
- return _connectionString;
- }
-
- set
- {
- _connectionString = value;
- }
- }
-
- public override bool Equals(object obj)
- {
- if (obj == null)
- {
- return false;
- }
-
- return (obj is DbConnectionPoolKey key && _connectionString == key._connectionString);
- }
-
- public override int GetHashCode()
- {
- return _connectionString == null ? 0 : _connectionString.GetHashCode();
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/MultipartIdentifier.cs b/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/MultipartIdentifier.cs
deleted file mode 100644
index a7c106baed..0000000000
--- a/src/Microsoft.Data.SqlClient/netfx/src/Common/src/Microsoft/Data/Common/MultipartIdentifier.cs
+++ /dev/null
@@ -1,293 +0,0 @@
-// 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.
-
-namespace Microsoft.Data.Common
-{
- using System;
- using System.Text;
-
- internal class MultipartIdentifier
- {
- private const int MaxParts = 4;
- internal const int ServerIndex = 0;
- internal const int CatalogIndex = 1;
- internal const int SchemaIndex = 2;
- internal const int TableIndex = 3;
-
- /*
- Left quote strings need to correspond 1 to 1 with the right quote strings
- example: "ab" "cd", passed in for the left and the right quote
- would set a or b as a starting quote character.
- If a is the starting quote char then c would be the ending quote char
- otherwise if b is the starting quote char then d would be the ending quote character.
- */
- internal static string[] ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, string property, bool ThrowOnEmptyMultipartName)
- {
- return ParseMultipartIdentifier(name, leftQuote, rightQuote, '.', MaxParts, true, property, ThrowOnEmptyMultipartName);
- }
-
- private enum MPIState
- {
- MPI_Value,
- MPI_ParseNonQuote,
- MPI_LookForSeparator,
- MPI_LookForNextCharOrSeparator,
- MPI_ParseQuote,
- MPI_RightQuote,
- }
-
- /* Core function for parsing the multipart identifier string.
- * parameters: name - string to parse
- * leftquote: set of characters which are valid quoteing characters to initiate a quote
- * rightquote: set of characters which are valid to stop a quote, array index's correspond to the the leftquote array.
- * separator: separator to use
- * limit: number of names to parse out
- * removequote:to remove the quotes on the returned string
- */
- private static void IncrementStringCount(string name, string[] ary, ref int position, string property)
- {
- ++position;
- int limit = ary.Length;
- if (position >= limit)
- {
- throw ADP.InvalidMultipartNameToManyParts(property, name, limit);
- }
- ary[position] = string.Empty;
- }
-
- private static bool IsWhitespace(char ch)
- {
- return Char.IsWhiteSpace(ch);
- }
-
- internal static string[] ParseMultipartIdentifier(string name, string leftQuote, string rightQuote, char separator, int limit, bool removequotes, string property, bool ThrowOnEmptyMultipartName)
- {
-
- if (limit <= 0)
- {
- throw ADP.InvalidMultipartNameToManyParts(property, name, limit);
- }
-
- if (-1 != leftQuote.IndexOf(separator) || -1 != rightQuote.IndexOf(separator) || leftQuote.Length != rightQuote.Length)
- {
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
-
- string[] parsedNames = new string[limit]; // return string array
- int stringCount = 0; // index of current string in the buffer
- MPIState state = MPIState.MPI_Value; // Initialize the starting state
-
- StringBuilder sb = new StringBuilder(name.Length); // String buffer to hold the string being currently built, init the string builder so it will never be resized
- StringBuilder whitespaceSB = null; // String buffer to hold white space used when parsing nonquoted strings 'a b . c d' = 'a b' and 'c d'
- char rightQuoteChar = ' '; // Right quote character to use given the left quote character found.
- for (int index = 0; index < name.Length; ++index)
- {
- char testchar = name[index];
- switch (state)
- {
- case MPIState.MPI_Value:
- {
- int quoteIndex;
- if (IsWhitespace(testchar))
- { // Is White Space then skip the whitespace
- continue;
- }
- else
- if (testchar == separator)
- { // If we found a separator, no string was found, initialize the string we are parsing to Empty and the next one to Empty.
- // This is NOT a redundant setting of string.Empty it solves the case where we are parsing ".foo" and we should be returning null, null, empty, foo
- parsedNames[stringCount] = string.Empty;
- IncrementStringCount(name, parsedNames, ref stringCount, property);
- }
- else
- if (-1 != (quoteIndex = leftQuote.IndexOf(testchar)))
- { // If we are a left quote
- rightQuoteChar = rightQuote[quoteIndex]; // record the corresponding right quote for the left quote
- sb.Length = 0;
- if (!removequotes)
- {
- sb.Append(testchar);
- }
- state = MPIState.MPI_ParseQuote;
- }
- else
- if (-1 != rightQuote.IndexOf(testchar))
- { // If we shouldn't see a right quote
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
- else
- {
- sb.Length = 0;
- sb.Append(testchar);
- state = MPIState.MPI_ParseNonQuote;
- }
- break;
- }
-
- case MPIState.MPI_ParseNonQuote:
- {
- if (testchar == separator)
- {
- parsedNames[stringCount] = sb.ToString(); // set the currently parsed string
- IncrementStringCount(name, parsedNames, ref stringCount, property);
- state = MPIState.MPI_Value;
- }
- else // Quotes are not valid inside a non-quoted name
- if (-1 != rightQuote.IndexOf(testchar))
- {
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
- else
- if (-1 != leftQuote.IndexOf(testchar))
- {
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
- else
- if (IsWhitespace(testchar))
- { // If it is Whitespace
- parsedNames[stringCount] = sb.ToString(); // Set the currently parsed string
- if (null == whitespaceSB)
- {
- whitespaceSB = new StringBuilder();
- }
- whitespaceSB.Length = 0;
- whitespaceSB.Append(testchar); // start to record the white space, if we are parsing a name like "foo bar" we should return "foo bar"
- state = MPIState.MPI_LookForNextCharOrSeparator;
- }
- else
- {
- sb.Append(testchar);
- }
- break;
- }
-
- case MPIState.MPI_LookForNextCharOrSeparator:
- {
- if (!IsWhitespace(testchar))
- { // If it is not whitespace
- if (testchar == separator)
- {
- IncrementStringCount(name, parsedNames, ref stringCount, property);
- state = MPIState.MPI_Value;
- }
- else
- { // If its not a separator and not whitespace
- sb.Append(whitespaceSB);
- sb.Append(testchar);
- parsedNames[stringCount] = sb.ToString(); // Need to set the name here in case the string ends here.
- state = MPIState.MPI_ParseNonQuote;
- }
- }
- else
- {
- whitespaceSB.Append(testchar);
- }
- break;
- }
-
- case MPIState.MPI_ParseQuote:
- {
- if (testchar == rightQuoteChar)
- { // if se are on a right quote see if we are escaping the right quote or ending the quoted string
- if (!removequotes)
- {
- sb.Append(testchar);
- }
- state = MPIState.MPI_RightQuote;
- }
- else
- {
- sb.Append(testchar); // Append what we are currently parsing
- }
- break;
- }
-
- case MPIState.MPI_RightQuote:
- {
- if (testchar == rightQuoteChar)
- { // If the next char is a another right quote then we were escaping the right quote
- sb.Append(testchar);
- state = MPIState.MPI_ParseQuote;
- }
- else
- if (testchar == separator)
- { // If its a separator then record what we've parsed
- parsedNames[stringCount] = sb.ToString();
- IncrementStringCount(name, parsedNames, ref stringCount, property);
- state = MPIState.MPI_Value;
- }
- else
- if (!IsWhitespace(testchar))
- { // If it is not white space we got problems
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
- else
- { // It is a whitespace character so the following char should be whitespace, separator, or end of string anything else is bad
- parsedNames[stringCount] = sb.ToString();
- state = MPIState.MPI_LookForSeparator;
- }
- break;
- }
-
- case MPIState.MPI_LookForSeparator:
- {
- if (!IsWhitespace(testchar))
- { // If it is not whitespace
- if (testchar == separator)
- { // If it is a separator
- IncrementStringCount(name, parsedNames, ref stringCount, property);
- state = MPIState.MPI_Value;
- }
- else
- { // Otherwise not a separator
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
- }
- break;
- }
- }
- }
-
- // Resolve final states after parsing the string
- switch (state)
- {
- case MPIState.MPI_Value: // These states require no extra action
- case MPIState.MPI_LookForSeparator:
- case MPIState.MPI_LookForNextCharOrSeparator:
- break;
-
- case MPIState.MPI_ParseNonQuote: // Dump what ever was parsed
- case MPIState.MPI_RightQuote:
- parsedNames[stringCount] = sb.ToString();
- break;
-
- case MPIState.MPI_ParseQuote: // Invalid Ending States
- default:
- throw ADP.InvalidMultipartNameIncorrectUsageOfQuotes(property, name);
- }
-
- if (parsedNames[0] == null)
- {
- if (ThrowOnEmptyMultipartName)
- {
- throw ADP.InvalidMultipartName(property, name); // Name is entirely made up of whitespace
- }
- }
- else
- {
- // Shuffle the parsed name, from left justification to right justification, ie [a][b][null][null] goes to [null][null][a][b]
- int offset = limit - stringCount - 1;
- if (offset > 0)
- {
- for (int x = limit - 1; x >= offset; --x)
- {
- parsedNames[x] = parsedNames[x - offset];
- parsedNames[x - offset] = null;
- }
- }
- }
- return parsedNames;
- }
- }
-}
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 8096839c85..7998f01935 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -103,6 +103,12 @@
Microsoft\Data\Common\ActivityCorrelator.cs
+
+ Microsoft\Data\Common\DbConnectionPoolKey.cs
+
+
+ Microsoft\Data\Common\MultipartIdentifier.cs
+
Microsoft\Data\Sql\SqlNotificationRequest.cs
@@ -417,8 +423,6 @@
-
-
@@ -535,4 +539,4 @@
-
\ No newline at end of file
+
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionPoolKey.cs
similarity index 100%
rename from src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionPoolKey.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionPoolKey.cs
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/MultipartIdentifier.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/MultipartIdentifier.cs
similarity index 100%
rename from src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/MultipartIdentifier.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/MultipartIdentifier.cs
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
index ed83505b6a..c41a99cc4d 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
@@ -26,6 +26,7 @@
+
@@ -55,6 +56,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultipartIdentifierTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultipartIdentifierTests.cs
new file mode 100644
index 0000000000..6e6ba3a751
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultipartIdentifierTests.cs
@@ -0,0 +1,260 @@
+// 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 Microsoft.Data.Common;
+using Xunit;
+
+namespace Microsoft.Data.SqlClient.Tests
+{
+ public class MultipartIdentifierTests
+ {
+ [Fact]
+ public void SingleUnquoted() => RunParse("foo", new[] { "foo" });
+
+ [Fact]
+ public void SingleUnquotedOvercount() => RunParse("foo", new[] { null, "foo" }, maxCount: 2);
+
+ [Fact]
+ public void SingleUnquotedContainsWhitespace() => RunParse("foo bar", new[] { "foo bar" });
+
+ [Fact]
+ public void SingleUnquotedStartWithShitespace() => RunParse(" foo", new[] { "foo" });
+
+ [Fact]
+ public void SingleUnquotedEndWithShitespace() => RunParse("foo ", new[] { "foo" });
+
+ [Fact]
+ public void SingleQuotedRemoveQuote() => RunParse("[foo]", new[] { "[foo]" }, false);
+
+ [Fact]
+ public void SingleQuotedKeepQuote() => RunParse("[foo]", new[] { "foo" }, true);
+
+ [Fact]
+ public void SingleQuotedLeadingWhitespace() => RunParse("[ foo]", new[] { " foo" }, true);
+
+ [Fact]
+ public void SingleQuotedTrailingWhitespace() => RunParse("[foo ]", new[] { "foo " }, true);
+
+ [Fact]
+ public void QuotedContainsWhitespace() => RunParse("[foo bar]", new[] { "foo bar" }, true);
+
+ [Fact]
+ public void SingleQuotedContainsAndTrailingWhitespace() => RunParse("[foo bar ]", new[] { "foo bar " });
+
+ [Fact]
+ public void SingleQuotedInternalAndLeadingWhitespace() => RunParse("[ foo bar]", new[] { " foo bar" });
+
+ [Fact]
+ public void SingleQuotedContainsAndLeadingAndTrailingWhitespace() => RunParse("[ foo bar ]", new[] { " foo bar " });
+
+ [Fact]
+ public void SingleQuotedEscapedQuote() => RunParse("[foo]]bar]", new[] { "foo]bar" }, true);
+
+
+ [Fact]
+ public void DoubleUnquotedParts() => RunParse("foo.bar", new[] { "foo", "bar" });
+
+ [Fact]
+ public void DoubleUnquotedPartContainsTrailngWhitespace() => RunParse("foo .bar", new[] { "foo", "bar" });
+
+ [Fact]
+ public void DoubleUnquotedPartContainsLeadingWhitespace() => RunParse("foo. bar", new[] { "foo", "bar" });
+
+ [Fact]
+ public void DoubleUnquotedEmptyFirst() => RunParse(".bar", new[] { "", "bar" });
+
+ [Fact]
+ public void DoubleUnquotedEmptyLast() => RunParse("foo.", new[] { "foo", "" });
+
+ [Fact]
+ public void DoubleQuotedParts() => RunParse("[foo].[bar]", new string[] { "foo", "bar" });
+
+ [Fact]
+ public void DoubleQuotedPartContainsLeadingWhitespace() => RunParse("[foo]. [bar]", new[] { "foo", "bar" });
+
+ [Fact]
+ public void DoubleQuotedPartContainsTrailngWhitespace() => RunParse("[foo] .[bar]", new[] { "foo", "bar" });
+
+
+ [Fact]
+ public void TripleUnquotedParts() => RunParse("foo.bar.ed", new[] { "foo", "bar", "ed" });
+
+ [Fact]
+ public void TripleUnquotedMissingMiddle() => RunParse("foo..bar", new[] { "foo", "", "bar" });
+
+ [Fact]
+ public void TripleUnquotedPartContainsTrailingWhitespace() => RunParse("foo .bar .ed", new[] { "foo", "bar", "ed" });
+
+ [Fact]
+ public void TripleUnquotedPartContainsEmptyAndTrailngWhitespace() => RunParse(" .bar .ed", new[] { "", "bar", "ed" });
+
+ [Fact]
+ public void TripleUnquotedPartContainsLeadingWhitespace() => RunParse("foo. bar.", new[] { "foo", "bar", "" });
+
+ [Fact]
+ public void TripleUnquotedEmptyPart() => RunParse(".bar", new[] { "", "bar" });
+
+ [Fact]
+ public void TripleQuotedParts() => RunParse("[foo].[bar]", new[] { "foo", "bar" });
+
+ [Fact]
+ public void TripleQuotedPartContainsLeadingWhitespace() => RunParse("[foo]. [bar]", new[] { "foo", "bar" });
+
+ [Fact]
+ public void TripleQuotedPartContainsTrailngWhitespace() => RunParse("[foo] .[bar]", new[] { "foo", "bar" });
+
+ [Fact]
+ public void InvalidUnquotedEmpty() => ThrowParse("", new[] { "" });
+
+ [Fact]
+ public void InvalidContainsOpen() => ThrowParse("foo[bar", new[] { "foo[bar" });
+
+ [Fact]
+ public void InvalidContainsClose() => ThrowParse("foo]bar", new[] { "foo]bar" });
+
+ [Fact]
+ public void InvalidStartsWithClose() => ThrowParse("]bar", new[] { "]bar" });
+
+ [Fact]
+ public void InvalidEndsWithClose() => ThrowParse("bar]", new[] { "bar]" });
+
+ [Fact]
+ public void InvalidUnfinishedBraceOpen() => ThrowParse("[foo", new[] { "[foo" });
+
+ [Fact]
+ public void InvalidUnfinishedQuoteOpen() => ThrowParse("\"foo", new[] { "\"foo" });
+
+ [Fact]
+ public void InvalidCapacity()
+ {
+ ThrowParse("", Array.Empty());
+ }
+
+ [Fact]
+ public void InvalidLeftQuote()
+ {
+ ThrowParse("foo", new[] { "foo" }, leftQuotes: "[.");
+ }
+
+ [Fact]
+ public void InvalidRightQuote()
+ {
+ ThrowParse("foo", new[] { "foo" }, rightQuotes: "[.");
+ }
+
+ [Fact]
+ public void InvalidQuotedPartContainsTrailngNonWhitespace() => ThrowParse("[foo]!.[bar]", new[] { "foo", "bar" });
+
+ [Fact]
+ public void InvalidQuotedPartContainsTrailngWhiteSpaceThenNonWhitespace() => ThrowParse("[foo] !.[bar]", new[] { "foo", "bar" });
+
+ [Fact]
+ public void InvalidTooManyParts_2to1() => ThrowParse("foo.bar", new[] { "foo" });
+
+ [Fact]
+ public void InvalidTooManyPartsEndsInSeparator() => ThrowParse("a.", 1);
+
+ [Fact]
+ public void InvalidTooManyPartsAfterTrailingWhitespace() => ThrowParse("foo .bar .ed", 1);
+
+ [Fact]
+ public void InvalidTooManyPartsEndsWithCloseQuote() => ThrowParse("a.[b]", 1);
+
+ [Fact]
+ public void InvalidTooManyPartsEndsWithWhitespace() => ThrowParse("a.foo ", 1);
+
+ [Fact]
+ public void InvalidTooManyPartsQuotedPartContainsLeadingWhitespace() => ThrowParse("a.[b].c", 1);
+
+ [Fact]
+ public void InvalidTooManyPartsWhiteSpaceBeforeSeparator() => ThrowParse("a.b ..", 2);
+
+ [Fact]
+ public void InvalidTooManyPartsAfterCloseQuote() => ThrowParse("a.[b] .c", 1);
+
+ [Fact]
+ public void InvalidTooManyPartsSeparatorAfterPart() => ThrowParse("a.b.c", 1);
+
+
+ private static void RunParse(string name, string[] expected, bool removeQuotes = true, int maxCount = 0)
+ {
+ if (maxCount == 0)
+ {
+ for (int index = 0; index < expected.Length; index++)
+ {
+ if (expected[index] != null)
+ {
+ maxCount += 1;
+ }
+ }
+ }
+
+ string[] originalParts = MultipartIdentifier.ParseMultipartIdentifier(name, "[\"", "]\"", '.', maxCount, removeQuotes, "", true);
+
+ for (int index = 0; index < expected.Length; index++)
+ {
+ string expectedPart = expected[index];
+ string originalPart = originalParts[index];
+
+ Assert.Equal(expectedPart, originalPart);
+ }
+ }
+
+ private static void ThrowParse(string name, string[] expected, bool removeQuotes = true, string leftQuotes = "[\"", string rightQuotes = "]\"", char separator = '.')
+ where TException : Exception
+ {
+ int maxCount = 0;
+ for (int index = 0; index < expected.Length; index++)
+ {
+ if (expected[index] != null)
+ {
+ maxCount += 1;
+ }
+ }
+
+ Exception originalException = Assert.Throws(() =>
+ MultipartIdentifier.ParseMultipartIdentifier(name, leftQuotes, rightQuotes, separator, maxCount, removeQuotes, "", true)
+ );
+
+ Assert.NotNull(originalException);
+ }
+
+
+
+ private static void ThrowParse(string name, int expectedLength, bool removeQuotes = true, string leftQuotes = "[\"", string rightQuotes = "]\"", char separator = '.')
+ {
+ Exception originalException = Assert.Throws(
+ () =>
+ {
+ MultipartIdentifier.ParseMultipartIdentifier(name, leftQuotes, rightQuotes, separator, expectedLength, removeQuotes, "test", true);
+ }
+ );
+ Assert.NotNull(originalException);
+ }
+
+ }
+}
+
+namespace Microsoft.Data.Common
+{
+ // this is needed for the inclusion of MultipartIdentifier class
+ internal class ADP
+ {
+ internal static ArgumentException InvalidMultipartName(string property, string name)
+ {
+ return new ArgumentException();
+ }
+
+ internal static ArgumentException InvalidMultipartNameIncorrectUsageOfQuotes(string property, string name)
+ {
+ return new ArgumentException();
+ }
+
+ internal static ArgumentException InvalidMultipartNameToManyParts(string property, string name, int limit)
+ {
+ return new ArgumentException();
+ }
+ }
+}