File: System\Data\Odbc\DbDataRecord.cs
Web Access
Project: src\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;
            }
        }
    }
}