Ejemplo n.º 1
0
        internal async Task <ReadOnlyCollection <NpgsqlDbColumn> > GetColumnSchema(bool async, CancellationToken cancellationToken = default)
        {
            // This is mainly for Amazon Redshift
            var oldQueryMode = _connection.PostgreSqlVersion < new Version(8, 2);

            var numFields = _rowDescription.Count;
            var result    = new List <NpgsqlDbColumn?>(numFields);

            for (var i = 0; i < numFields; i++)
            {
                result.Add(null);
            }
            var populatedColumns = 0;

            if (_fetchAdditionalInfo)
            {
                // 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
                                        .Where(f => f.TableOID != 0)  // Only column fields
                                        .Select(c => $"(attr.attrelid={c.TableOID} AND attr.attnum={c.ColumnAttributeNumber})")
                                        .Join(" OR ");

                if (columnFieldFilter != string.Empty)
                {
                    var query = oldQueryMode
                                                ? GenerateOldColumnsQuery(columnFieldFilter)
                                                : GenerateColumnsQuery(_connection.PostgreSqlVersion, columnFieldFilter);

                    using var scope = new TransactionScope(
                              TransactionScopeOption.Suppress,
                              async ? TransactionScopeAsyncFlowOption.Enabled : TransactionScopeAsyncFlowOption.Suppress);
                    using var connection = (NpgsqlConnection)((ICloneable)_connection).Clone();

                    await connection.Open(async, cancellationToken);

                    using var cmd    = new NpgsqlCommand(query, connection);
                    using var reader = await cmd.ExecuteReader(CommandBehavior.Default, async, cancellationToken);

                    while (async ? await reader.ReadAsync(cancellationToken): reader.Read())
                    {
                        var column = LoadColumnDefinition(reader, _connection.Connector !.TypeMapper.DatabaseInfo, oldQueryMode);
                        for (var ordinal = 0; ordinal < numFields; ordinal++)
                        {
                            var field = _rowDescription[ordinal];
                            if (field.TableOID == column.TableOID &&
                                field.ColumnAttributeNumber == column.ColumnAttributeNumber)
                            {
                                populatedColumns++;

                                if (column.ColumnOrdinal.HasValue)
                                {
                                    column = column.Clone();
                                }

                                // The column's ordinal is with respect to the resultset, not its table
                                column.ColumnOrdinal = ordinal;
                                result[ordinal]      = column;
                            }
                        }
                    }
                }
            }

            // 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 < numFields; i++)
            {
                var column = result[i];
                var field  = _rowDescription[i];

                if (column is null)
                {
                    column = SetUpNonColumnField(field);
                    column.ColumnOrdinal = i;
                    result[i]            = column;
                    populatedColumns++;
                }

                column.ColumnName = field.Name;
                column.IsAliased  = column.BaseColumnName is null ? default(bool?) : (column.BaseColumnName != column.ColumnName);
            }

            if (populatedColumns != numFields)
            {
                throw new NpgsqlException("Could not load all columns for the resultset");
            }

            return(result.AsReadOnly() !);
        }