private void GetActualFieldsAndProperties(out List<MSS.SmiExtendedMetaData> fields, out SmiMetaDataPropertyCollection props, out ParameterPeekAheadValue peekAhead)
{
fields = null;
props = null;
peekAhead = null;
object value = GetCoercedValue();
if (value is SqlDataReader)
{
fields = new List<MSS.SmiExtendedMetaData>(((SqlDataReader)value).GetInternalSmiMetaData());
if (fields.Count <= 0)
{
throw SQL.NotEnoughColumnsInStructuredType();
}
bool[] keyCols = new bool[fields.Count];
bool hasKey = false;
for (int i = 0; i < fields.Count; i++)
{
MSS.SmiQueryMetaData qmd = fields[i] as MSS.SmiQueryMetaData;
if (null != qmd && !qmd.IsKey.IsNull && qmd.IsKey.Value)
{
keyCols[i] = true;
hasKey = true;
}
}
// Add unique key property, if any found.
if (hasKey)
{
props = new SmiMetaDataPropertyCollection();
props[MSS.SmiPropertySelector.UniqueKey] = new MSS.SmiUniqueKeyProperty(new List<bool>(keyCols));
}
}
else if (value is IEnumerable<SqlDataRecord>)
{
// must grab the first record of the enumerator to get the metadata
IEnumerator<MSS.SqlDataRecord> enumerator = ((IEnumerable<MSS.SqlDataRecord>)value).GetEnumerator();
MSS.SqlDataRecord firstRecord = null;
try
{
// no need for fields if there's no rows or no columns -- we'll be sending a null instance anyway.
if (enumerator.MoveNext())
{
firstRecord = enumerator.Current;
int fieldCount = firstRecord.FieldCount;
if (0 < fieldCount)
{
// It's valid! Grab those fields.
bool[] keyCols = new bool[fieldCount];
bool[] defaultFields = new bool[fieldCount];
bool[] sortOrdinalSpecified = new bool[fieldCount];
int maxSortOrdinal = -1; // largest sort ordinal seen, used to optimize locating holes in the list
bool hasKey = false;
bool hasDefault = false;
int sortCount = 0;
SmiOrderProperty.SmiColumnOrder[] sort = new SmiOrderProperty.SmiColumnOrder[fieldCount];
fields = new List<MSS.SmiExtendedMetaData>(fieldCount);
for (int i = 0; i < fieldCount; i++)
{
SqlMetaData colMeta = firstRecord.GetSqlMetaData(i);
fields.Add(MSS.MetaDataUtilsSmi.SqlMetaDataToSmiExtendedMetaData(colMeta));
if (colMeta.IsUniqueKey)
{
keyCols[i] = true;
hasKey = true;
}
if (colMeta.UseServerDefault)
{
defaultFields[i] = true;
hasDefault = true;
}
sort[i].Order = colMeta.SortOrder;
if (SortOrder.Unspecified != colMeta.SortOrder)
{
// SqlMetaData takes care of checking for negative sort ordinals with specified sort order
// bail early if there's no way sort order could be monotonically increasing
if (fieldCount <= colMeta.SortOrdinal)
{
throw SQL.SortOrdinalGreaterThanFieldCount(i, colMeta.SortOrdinal);
}
// Check to make sure we haven't seen this ordinal before
if (sortOrdinalSpecified[colMeta.SortOrdinal])
{
throw SQL.DuplicateSortOrdinal(colMeta.SortOrdinal);
}
sort[i].SortOrdinal = colMeta.SortOrdinal;
sortOrdinalSpecified[colMeta.SortOrdinal] = true;
if (colMeta.SortOrdinal > maxSortOrdinal)
{
maxSortOrdinal = colMeta.SortOrdinal;
}
sortCount++;
}
}
if (hasKey)
{
props = new SmiMetaDataPropertyCollection();
props[MSS.SmiPropertySelector.UniqueKey] = new MSS.SmiUniqueKeyProperty(new List<bool>(keyCols));
}
if (hasDefault)
{
// May have already created props list in unique key handling
if (null == props)
{
props = new SmiMetaDataPropertyCollection();
}
props[MSS.SmiPropertySelector.DefaultFields] = new MSS.SmiDefaultFieldsProperty(new List<bool>(defaultFields));
}
if (0 < sortCount)
{
// validate monotonically increasing sort order.
// Since we already checked for duplicates, we just need
// to watch for values outside of the sortCount range.
if (maxSortOrdinal >= sortCount)
{
// there is at least one hole, find the first one
int i;
for (i = 0; i < sortCount; i++)
{
if (!sortOrdinalSpecified[i])
{
break;
}
}
Debug.Assert(i < sortCount, "SqlParameter.GetActualFieldsAndProperties: SortOrdinal hole-finding algorithm failed!");
throw SQL.MissingSortOrdinal(i);
}
// May have already created props list
if (null == props)
{
props = new SmiMetaDataPropertyCollection();
}
props[MSS.SmiPropertySelector.SortOrder] = new MSS.SmiOrderProperty(
new List<SmiOrderProperty.SmiColumnOrder>(sort));
}
// pack it up so we don't have to rewind to send the first value
peekAhead = new ParameterPeekAheadValue();
peekAhead.Enumerator = enumerator;
peekAhead.FirstRecord = firstRecord;
// now that it's all packaged, make sure we don't dispose it.
enumerator = null;
}
else
{
throw SQL.NotEnoughColumnsInStructuredType();
}
}
else
{
throw SQL.IEnumerableOfSqlDataRecordHasNoRows();
}
}
finally
{
if (enumerator != null)
{
enumerator.Dispose();
}
}
}
else if (value is DbDataReader)
{
// For ProjectK\CoreCLR, DbDataReader no longer supports GetSchema
// So instead we will attempt to generate the metadata from the Field Type alone
var reader = (DbDataReader)value;
if (reader.FieldCount <= 0)
{
throw SQL.NotEnoughColumnsInStructuredType();
}
fields = new List<MSS.SmiExtendedMetaData>(reader.FieldCount);
for (int i = 0; i < reader.FieldCount; i++)
{
fields.Add(MSS.MetaDataUtilsSmi.SmiMetaDataFromType(reader.GetName(i), reader.GetFieldType(i)));
}
}
}