internal ReadOnlyCollection<NpgsqlDbColumn> GetColumnSchema()
{
var fields = _rowDescription.Fields;
if (fields.Count == 0)
return new List<NpgsqlDbColumn>().AsReadOnly();
var result = new List<NpgsqlDbColumn>(fields.Count);
for (var i = 0; i < fields.Count; i++)
result.Add(null);
var populatedColumns = 0;
// We have two types of fields - those which correspond to actual database columns
// and those that don't (e.g. SELECT 8). For the former we load lots of info from
// the backend (if fetchAdditionalInfo is true), for the latter we only have the RowDescription
var columnFieldFilter = _rowDescription.Fields
.Where(f => f.TableOID != 0) // Only column fields
.Select(c => $"(attr.attrelid={c.TableOID} AND attr.attnum={c.ColumnAttributeNumber})")
.Join(" OR ");
if (_fetchAdditionalInfo && columnFieldFilter != "")
{
var query = string.Format(GetColumnsQuery, columnFieldFilter);
#if NET45 || NET451
using (var connection = (NpgsqlConnection)((ICloneable)_connection).Clone())
#else
using (var connection = _connection.Clone())
#endif
{
connection.Open();
using (var cmd = new NpgsqlCommand(query, connection))
using (var reader = cmd.ExecuteReader())
{
for (; reader.Read(); populatedColumns++)
{
var column = LoadColumnDefinition(reader);
var ordinal = fields.FindIndex(f => f.TableOID == column.TableOID && f.ColumnAttributeNumber - 1 == column.ColumnAttributeNumber);
Contract.Assert(ordinal >= 0);
var field = fields[ordinal];
Contract.Assert(field.Name == column.ColumnName);
// The column's ordinal is with respect to the resultset, not its table
column.ColumnOrdinal = ordinal;
result[ordinal] = column;
}
}
if (populatedColumns == fields.Count)
{
// All columns were regular table columns that got loaded, we're done
Contract.Assert(result.All(c => c != null));
return result.AsReadOnly();
}
}
}
// We had some fields which don't correspond to regular table columns (or fetchAdditionalInfo is false).
// Fill in whatever info we have from the RowDescription itself
for (var i = 0; i < fields.Count; i++)
{
if (result[i] != null)
continue;
var column = SetUpNonColumnField(fields[i]);
column.ColumnOrdinal = i;
result[i] = column;
populatedColumns++;
}
if (populatedColumns != fields.Count)
throw new NpgsqlException("Could not load all columns for the resultset");
return result.AsReadOnly();
}