diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 09061277e0..d081aab9b6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -329,8 +329,8 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() if (null != metaData && 0 < metaData.Length) { - metaDataReturn = new SmiExtendedMetaData[metaData.visibleColumns]; - + metaDataReturn = new SmiExtendedMetaData[metaData.VisibleColumnCount]; + int returnIndex = 0; for (int index = 0; index < metaData.Length; index++) { _SqlMetaData colMetaData = metaData[index]; @@ -369,7 +369,7 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() length /= ADP.CharSize; } - metaDataReturn[index] = new SmiQueryMetaData( + metaDataReturn[returnIndex] = new SmiQueryMetaData( colMetaData.type, length, colMetaData.precision, @@ -397,6 +397,7 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() colMetaData.IsDifferentName, colMetaData.IsHidden ); + returnIndex += 1; } } } @@ -458,7 +459,7 @@ override public int VisibleFieldCount { return 0; } - return (md.visibleColumns); + return md.VisibleColumnCount; } } @@ -1352,31 +1353,6 @@ private bool TryConsumeMetaData() Debug.Assert(!ignored, "Parser read a row token while trying to read metadata"); } - // we hide hidden columns from the user so build an internal map - // that compacts all hidden columns from the array - if (null != _metaData) - { - - if (_snapshot != null && object.ReferenceEquals(_snapshot._metadata, _metaData)) - { - _metaData = (_SqlMetaDataSet)_metaData.Clone(); - } - - _metaData.visibleColumns = 0; - - Debug.Assert(null == _metaData.indexMap, "non-null metaData indexmap"); - int[] indexMap = new int[_metaData.Length]; - for (int i = 0; i < indexMap.Length; ++i) - { - indexMap[i] = _metaData.visibleColumns; - - if (!(_metaData[i].IsHidden)) - { - _metaData.visibleColumns++; - } - } - _metaData.indexMap = indexMap; - } return true; } @@ -1690,15 +1666,15 @@ override public DataTable GetSchemaTable() try { statistics = SqlStatistics.StartTimer(Statistics); - if (null == _metaData || null == _metaData.schemaTable) + if (null == _metaData || null == _metaData._schemaTable) { if (null != this.MetaData) { - _metaData.schemaTable = BuildSchemaTable(); - Debug.Assert(null != _metaData.schemaTable, "No schema information yet!"); + _metaData._schemaTable = BuildSchemaTable(); + Debug.Assert(null != _metaData._schemaTable, "No schema information yet!"); } } - return _metaData?.schemaTable; + return _metaData?._schemaTable; } finally { @@ -2994,11 +2970,11 @@ virtual public int GetSqlValues(object[] values) SetTimeout(_defaultTimeoutMilliseconds); - int copyLen = (values.Length < _metaData.visibleColumns) ? values.Length : _metaData.visibleColumns; + int copyLen = (values.Length < _metaData.VisibleColumnCount) ? values.Length : _metaData.VisibleColumnCount; for (int i = 0; i < copyLen; i++) { - values[_metaData.indexMap[i]] = GetSqlValueInternal(i); + values[_metaData.GetVisibleColumnIndex(i)] = GetSqlValueInternal(i); } return copyLen; } @@ -3398,7 +3374,7 @@ override public int GetValues(object[] values) CheckMetaDataIsReady(); - int copyLen = (values.Length < _metaData.visibleColumns) ? values.Length : _metaData.visibleColumns; + int copyLen = (values.Length < _metaData.VisibleColumnCount) ? values.Length : _metaData.VisibleColumnCount; int maximumColumn = copyLen - 1; SetTimeout(_defaultTimeoutMilliseconds); @@ -3414,12 +3390,19 @@ override public int GetValues(object[] values) for (int i = 0; i < copyLen; i++) { // Get the usable, TypeSystem-compatible value from the iternal buffer - values[_metaData.indexMap[i]] = GetValueFromSqlBufferInternal(_data[i], _metaData[i]); + int fieldIndex = _metaData.GetVisibleColumnIndex(i); + values[i] = GetValueFromSqlBufferInternal(_data[fieldIndex], _metaData[fieldIndex]); // If this is sequential access, then we need to wipe the internal buffer if ((sequentialAccess) && (i < maximumColumn)) { _data[i].Clear(); + if (fieldIndex > i && fieldIndex > 0) + { + // if we jumped an index forward because of a hidden column see if the buffer before the + // current one was populated by the seek forward and clear it if it was + _data[fieldIndex - 1].Clear(); + } } } @@ -4767,7 +4750,7 @@ internal bool TrySetMetaData(_SqlMetaDataSet metaData, bool moreInfo) _tableNames = null; if (_metaData != null) { - _metaData.schemaTable = null; + _metaData._schemaTable = null; _data = SqlBuffer.CreateBufferArray(metaData.Length); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 6daca4d771..cd0fab3e2d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -5094,7 +5094,6 @@ internal bool TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, metaData = null; _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns, null); - int[] indexMap = new int[cColumns]; if (!stateObj.TryReadUInt16(out altMetaDataSet.id)) { @@ -5191,12 +5190,8 @@ internal bool TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, break; } } - indexMap[i] = i; } - altMetaDataSet.indexMap = indexMap; - altMetaDataSet.visibleColumns = cColumns; - metaData = altMetaDataSet; return true; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 21004f4be2..bf113efe3b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -511,51 +511,63 @@ public object Clone() } } - sealed internal class _SqlMetaDataSet : ICloneable + sealed internal class _SqlMetaDataSet { internal ushort id; // for altrow-columns only - internal int[] indexMap; - internal int visibleColumns; - internal DataTable schemaTable; + internal DataTable _schemaTable; internal readonly SqlTceCipherInfoTable cekTable; // table of "column encryption keys" used for this metadataset - internal readonly _SqlMetaData[] metaDataArray; + internal readonly _SqlMetaData[] _metaDataArray; + private int _hiddenColumnCount; + private int[] _visibleColumnMap; internal _SqlMetaDataSet(int count, SqlTceCipherInfoTable cipherTable) { + _hiddenColumnCount = -1; cekTable = cipherTable; - metaDataArray = new _SqlMetaData[count]; - for (int i = 0; i < metaDataArray.Length; ++i) + _metaDataArray = new _SqlMetaData[count]; + for (int i = 0; i < _metaDataArray.Length; ++i) { - metaDataArray[i] = new _SqlMetaData(i); + _metaDataArray[i] = new _SqlMetaData(i); } } private _SqlMetaDataSet(_SqlMetaDataSet original) { - this.id = original.id; - // although indexMap is not immutable, in practice it is initialized once and then passed around - this.indexMap = original.indexMap; - this.visibleColumns = original.visibleColumns; - this.schemaTable = original.schemaTable; - if (original.metaDataArray == null) + id = original.id; + _hiddenColumnCount = original._hiddenColumnCount; + _visibleColumnMap = original._visibleColumnMap; + _schemaTable = original._schemaTable; + if (original._metaDataArray == null) { - metaDataArray = null; + _metaDataArray = null; } else { - metaDataArray = new _SqlMetaData[original.metaDataArray.Length]; - for (int idx = 0; idx < metaDataArray.Length; idx++) + _metaDataArray = new _SqlMetaData[original._metaDataArray.Length]; + for (int idx = 0; idx < _metaDataArray.Length; idx++) { - metaDataArray[idx] = (_SqlMetaData)original.metaDataArray[idx].Clone(); + _metaDataArray[idx] = (_SqlMetaData)original._metaDataArray[idx].Clone(); } } } + internal int VisibleColumnCount + { + get + { + if (_hiddenColumnCount == -1) + { + SetupHiddenColumns(); + } + return Length - _hiddenColumnCount; + } + } + internal int Length { get { - return metaDataArray.Length; + return _metaDataArray.Length; } } @@ -563,21 +575,66 @@ internal _SqlMetaData this[int index] { get { - return metaDataArray[index]; + return _metaDataArray[index]; } set { Debug.Assert(null == value, "used only by SqlBulkCopy"); - metaDataArray[index] = value; + _metaDataArray[index] = value; } } - public object Clone() + public int GetVisibleColumnIndex(int index) + { + if (_hiddenColumnCount == -1) + { + SetupHiddenColumns(); + } + if (_visibleColumnMap is null) + { + return index; + } + else + { + return _visibleColumnMap[index]; + } + } + + public _SqlMetaDataSet Clone() { return new _SqlMetaDataSet(this); } + + private void SetupHiddenColumns() + { + int hiddenColumnCount = 0; + for (int index = 0; index < Length; index++) + { + if (_metaDataArray[index].IsHidden) + { + hiddenColumnCount += 1; + } + } + + if (hiddenColumnCount > 0) + { + int[] visibleColumnMap = new int[Length - hiddenColumnCount]; + int mapIndex = 0; + for (int metaDataIndex = 0; metaDataIndex < Length; metaDataIndex++) + { + if (!_metaDataArray[metaDataIndex].IsHidden) + { + visibleColumnMap[mapIndex] = metaDataIndex; + mapIndex += 1; + } + } + _visibleColumnMap = visibleColumnMap; + } + _hiddenColumnCount = hiddenColumnCount; + } } + sealed internal class _SqlMetaDataSetCollection : ICloneable { private readonly List<_SqlMetaDataSet> altMetaDataSetArray; @@ -622,10 +679,10 @@ internal _SqlMetaDataSet GetAltMetaData(int id) public object Clone() { _SqlMetaDataSetCollection result = new _SqlMetaDataSetCollection(); - result.metaDataSet = metaDataSet == null ? null : (_SqlMetaDataSet)metaDataSet.Clone(); + result.metaDataSet = metaDataSet == null ? null : metaDataSet.Clone(); foreach (_SqlMetaDataSet set in altMetaDataSetArray) { - result.altMetaDataSetArray.Add((_SqlMetaDataSet)set.Clone()); + result.altMetaDataSetArray.Add(set.Clone()); } return result; }