| File: System\Data\Odbc\DbDataRecord.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.Odbc\src\System.Data.Odbc.csproj (System.Data.Odbc) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Data.Odbc { internal sealed class DbSchemaInfo { internal DbSchemaInfo() { } internal string? _name; internal string? _typename; internal Type? _type; internal ODBC32.SQL_TYPE? _dbtype; } ///////////////////////////////////////////////////////////////////////////// // Cache // // This is a on-demand cache, only caching what the user requests. // The rationale is that for ForwardOnly access (the default and LCD of drivers) // we cannot obtain the data more than once, and even GetData(0) (to determine is-null) // still obtains data for fixed length types. // So simple code like: // if (!rReader.IsDBNull(i)) // rReader.GetInt32(i) // // Would fail, unless we cache on the IsDBNull call, and return the cached // item for GetInt32. This actually improves perf anyway, (even if the driver could // support it), since we are not making a separate interop call... // We do not cache all columns, so reading out of order is still not // ///////////////////////////////////////////////////////////////////////////// internal sealed class DbCache { //Data private readonly bool[] _isBadValue; private DbSchemaInfo?[]? _schema; private readonly object?[] _values; private readonly OdbcDataReader _record; internal int _count; internal bool _randomaccess = true; //Constructor internal DbCache(OdbcDataReader record, int count) { _count = count; _record = record; _randomaccess = (!record.IsBehavior(CommandBehavior.SequentialAccess)); _values = new object[count]; _isBadValue = new bool[count]; } //Accessor internal object? this[int i] { get { if (_isBadValue[i]) { OverflowException innerException = (OverflowException)Values[i]!; throw new OverflowException(innerException.Message, innerException); } return Values[i]; } set { Values[i] = value; _isBadValue[i] = false; } } internal int Count { get { return _count; } } internal void InvalidateValue(int i) { _isBadValue[i] = true; } internal object?[] Values { get { return _values; } } internal object? AccessIndex(int i) { //Note: We could put this directly in this[i], instead of having an explicit overload. //However that means that EVERY access into the cache takes the hit of checking, so //something as simple as the following code would take two hits. It's nice not to //have to take the hit when you know what your doing. // // if (cache[i] == null) // .... // return cache[i]; object?[] values = this.Values; if (_randomaccess) { //Random //Means that the user can ask for the values int any order (ie: out of order). // In order to achieve this on a forward only stream, we need to actually // retrieve all the value in between so they can go back to values they've skipped for (int c = 0; c < i; c++) { values[c] ??= _record.GetValue(c); } } return values[i]; } internal DbSchemaInfo GetSchema(int i) { _schema ??= new DbSchemaInfo[Count]; return _schema[i] ??= new DbSchemaInfo(); } internal void FlushValues() { //Set all objects to null (to explicitly release them) //Note: SchemaInfo remains the same for all rows - no need to reget those... int count = _values.Length; for (int i = 0; i < count; ++i) { _values[i] = null; } } } }