private object[] SetupSchemaWithKeyInfo(MissingMappingAction mappingAction, MissingSchemaAction schemaAction, bool gettingData, DataColumn parentChapterColumn, object chapterValue)
{
// must sort rows from schema table by ordinal because Jet is sorted by coumn name
DbSchemaRow[] schemaRows = DbSchemaRow.GetSortedSchemaRows(_schemaTable, _dataReader.ReturnProviderSpecificTypes);
Debug.Assert(null != schemaRows, "SchemaSetup - null DbSchemaRow[]");
Debug.Assert(_dataReader.FieldCount <= schemaRows.Length, "unexpected fewer rows in Schema than FieldCount");
if (0 == schemaRows.Length)
{
_dataTable = null;
return null;
}
// Everett behavior, always add a primary key if a primary key didn't exist before
// Whidbey behavior, same as Everett unless using LoadOption then add primary key only if no columns previously existed
bool addPrimaryKeys = (((0 == _dataTable.PrimaryKey.Length) && ((4 <= (int)_loadOption) || (0 == _dataTable.Rows.Count)))
|| (0 == _dataTable.Columns.Count));
DataColumn[] keys = null;
int keyCount = 0;
bool isPrimary = true; // assume key info (if any) is about a primary key
string keyBaseTable = null;
string commonBaseTable = null;
bool keyFromMultiTable = false;
bool commonFromMultiTable = false;
int[] columnIndexMap = null;
bool[] chapterIndexMap = null;
int mappingCount = 0;
object[] dataValues = null;
List<object> addedItems = null;
DataColumnCollection columnCollection = _dataTable.Columns;
try
{
for (int sortedIndex = 0; sortedIndex < schemaRows.Length; ++sortedIndex)
{
DbSchemaRow schemaRow = schemaRows[sortedIndex];
int unsortedIndex = schemaRow.UnsortedIndex;
bool ischapter = false;
Type fieldType = schemaRow.DataType;
if (null == fieldType)
{
fieldType = _dataReader.GetFieldType(sortedIndex);
}
if (null == fieldType)
{
throw ADP.MissingDataReaderFieldType(sortedIndex);
}
// if IDataReader, hierarchy exists and we will use an Int32,AutoIncrementColumn in this table
if (typeof(IDataReader).IsAssignableFrom(fieldType))
{
if (null == chapterIndexMap)
{
chapterIndexMap = new bool[schemaRows.Length];
}
chapterIndexMap[unsortedIndex] = ischapter = true;
fieldType = typeof(int);
}
else if (typeof(System.Data.SqlTypes.SqlXml).IsAssignableFrom(fieldType))
{
if (null == _xmlMap)
{
_xmlMap = new int[schemaRows.Length];
}
_xmlMap[sortedIndex] = SqlXml;
}
else if (typeof(System.Xml.XmlReader).IsAssignableFrom(fieldType))
{
fieldType = typeof(string);
if (null == _xmlMap)
{
_xmlMap = new int[schemaRows.Length];
}
_xmlMap[sortedIndex] = XmlDocument;
}
DataColumn dataColumn = null;
if (!schemaRow.IsHidden)
{
dataColumn = _tableMapping.GetDataColumn(_fieldNames[sortedIndex], fieldType, _dataTable, mappingAction, schemaAction);
}
string basetable = /*schemaRow.BaseServerName+schemaRow.BaseCatalogName+schemaRow.BaseSchemaName+*/ schemaRow.BaseTableName;
if (null == dataColumn)
{
if (null == columnIndexMap)
{
columnIndexMap = CreateIndexMap(schemaRows.Length, unsortedIndex);
}
columnIndexMap[unsortedIndex] = -1;
// if the column is not mapped and it is a key, then don't add any key information
if (schemaRow.IsKey)
{
#if DEBUG
if (AdapterSwitches.DataSchema.TraceVerbose)
{
Debug.WriteLine("SetupSchema: partial primary key detected");
}
#endif
// if the hidden key comes from a different table - don't throw away the primary key
// example SELECT [T2].[ID], [T2].[ProdID], [T2].[VendorName] FROM [Vendor] AS [T2], [Prod] AS [T1] WHERE (([T1].[ProdID] = [T2].[ProdID]))
if (keyFromMultiTable || (schemaRow.BaseTableName == keyBaseTable))
{
addPrimaryKeys = false; // don't add any future keys now
keys = null; // get rid of any keys we've seen
}
}
continue; // null means ignore (mapped to nothing)
}
else if ((null != _xmlMap) && (0 != _xmlMap[sortedIndex]))
{
if (typeof(System.Data.SqlTypes.SqlXml) == dataColumn.DataType)
{
_xmlMap[sortedIndex] = SqlXml;
}
else if (typeof(System.Xml.XmlDocument) == dataColumn.DataType)
{
_xmlMap[sortedIndex] = XmlDocument;
}
else
{
_xmlMap[sortedIndex] = 0; // datacolumn is not a specific Xml dataType, i.e. string
int total = 0;
for (int x = 0; x < _xmlMap.Length; ++x)
{
total += _xmlMap[x];
}
if (0 == total)
{ // not mapping to a specific Xml datatype, get rid of the map
_xmlMap = null;
}
}
}
if (schemaRow.IsKey)
{
if (basetable != keyBaseTable)
{
if (null == keyBaseTable)
{
keyBaseTable = basetable;
}
else keyFromMultiTable = true;
}
}
if (ischapter)
{
if (null == dataColumn.Table)
{
dataColumn.AllowDBNull = false;
dataColumn.AutoIncrement = true;
dataColumn.ReadOnly = true;
}
else if (!dataColumn.AutoIncrement)
{
throw ADP.FillChapterAutoIncrement();
}
}
else
{
if (!commonFromMultiTable)
{
if ((basetable != commonBaseTable) && (!string.IsNullOrEmpty(basetable)))
{
if (null == commonBaseTable)
{
commonBaseTable = basetable;
}
else
{
commonFromMultiTable = true;
}
}
}
if (4 <= (int)_loadOption)
{
if (schemaRow.IsAutoIncrement && DataColumn.IsAutoIncrementType(fieldType))
{
// CONSIDER: use T-SQL "IDENT_INCR('table_or_view')" and "IDENT_SEED('table_or_view')"
// functions to obtain the actual increment and seed values
dataColumn.AutoIncrement = true;
if (!schemaRow.AllowDBNull)
{
dataColumn.AllowDBNull = false;
}
}
// setup maxLength, only for string columns since this is all the DataSet supports
if (fieldType == typeof(string))
{
// schemaRow.Size is count of characters for string columns, count of bytes otherwise
dataColumn.MaxLength = schemaRow.Size > 0 ? schemaRow.Size : -1;
}
if (schemaRow.IsReadOnly)
{
dataColumn.ReadOnly = true;
}
if (!schemaRow.AllowDBNull && (!schemaRow.IsReadOnly || schemaRow.IsKey))
{
dataColumn.AllowDBNull = false;
}
if (schemaRow.IsUnique && !schemaRow.IsKey && !fieldType.IsArray)
{
// note, arrays are not comparable so only mark non-arrays as unique, ie timestamp columns
// are unique, but not comparable
dataColumn.Unique = true;
if (!schemaRow.AllowDBNull)
{
dataColumn.AllowDBNull = false;
}
}
}
else if (null == dataColumn.Table)
{
dataColumn.AutoIncrement = schemaRow.IsAutoIncrement;
dataColumn.AllowDBNull = schemaRow.AllowDBNull;
dataColumn.ReadOnly = schemaRow.IsReadOnly;
dataColumn.Unique = schemaRow.IsUnique;
if (fieldType == typeof(string) || (fieldType == typeof(SqlTypes.SqlString)))
{
// schemaRow.Size is count of characters for string columns, count of bytes otherwise
dataColumn.MaxLength = schemaRow.Size;
}
}
}
if (null == dataColumn.Table)
{
if (4 > (int)_loadOption)
{
AddAdditionalProperties(dataColumn, schemaRow.DataRow);
}
AddItemToAllowRollback(ref addedItems, dataColumn);
columnCollection.Add(dataColumn);
}
// The server sends us one key per table according to these rules.
//
// 1. If the table has a primary key, the server sends us this key.
// 2. If the table has a primary key and a unique key, it sends us the primary key
// 3. if the table has no primary key but has a unique key, it sends us the unique key
//
// In case 3, we will promote a unique key to a primary key IFF all the columns that compose
// that key are not nullable since no columns in a primary key can be null. If one or more
// of the keys is nullable, then we will add a unique constraint.
//
if (addPrimaryKeys && schemaRow.IsKey)
{
if (keys == null)
{
keys = new DataColumn[schemaRows.Length];
}
keys[keyCount++] = dataColumn;
#if DEBUG
if (AdapterSwitches.DataSchema.TraceVerbose)
{
Debug.WriteLine("SetupSchema: building list of " + ((isPrimary) ? "PrimaryKey" : "UniqueConstraint"));
}
#endif
// see case 3 above, we do want dataColumn.AllowDBNull not schemaRow.AllowDBNull
// otherwise adding PrimaryKey will change AllowDBNull to false
if (isPrimary && dataColumn.AllowDBNull)
{
#if DEBUG
if (AdapterSwitches.DataSchema.TraceVerbose)
{
Debug.WriteLine("SetupSchema: changing PrimaryKey into UniqueContraint");
}
#endif
isPrimary = false;
}
}
if (null != columnIndexMap)
{
columnIndexMap[unsortedIndex] = dataColumn.Ordinal;
}
else if (unsortedIndex != dataColumn.Ordinal)
{
columnIndexMap = CreateIndexMap(schemaRows.Length, unsortedIndex);
columnIndexMap[unsortedIndex] = dataColumn.Ordinal;
}
mappingCount++;
}
bool addDataRelation = false;
DataColumn chapterColumn = null;
if (null != chapterValue)
{ // add the extra column in the child table
Type fieldType = chapterValue.GetType();
chapterColumn = _tableMapping.GetDataColumn(_tableMapping.SourceTable, fieldType, _dataTable, mappingAction, schemaAction);
if (null != chapterColumn)
{
if (null == chapterColumn.Table)
{
chapterColumn.ReadOnly = true;
chapterColumn.AllowDBNull = false;
AddItemToAllowRollback(ref addedItems, chapterColumn);
columnCollection.Add(chapterColumn);
addDataRelation = (null != parentChapterColumn);
}
mappingCount++;
}
}
if (0 < mappingCount)
{
if ((null != _dataSet) && null == _dataTable.DataSet)
{
AddItemToAllowRollback(ref addedItems, _dataTable);
_dataSet.Tables.Add(_dataTable);
}
// setup the key
if (addPrimaryKeys && (null != keys))
{
if (keyCount < keys.Length)
{
keys = ResizeColumnArray(keys, keyCount);
}
if (isPrimary)
{
#if DEBUG
if (AdapterSwitches.DataSchema.TraceVerbose)
{
Debug.WriteLine("SetupSchema: set_PrimaryKey");
}
#endif
_dataTable.PrimaryKey = keys;
}
else
{
UniqueConstraint unique = new UniqueConstraint("", keys);
ConstraintCollection constraints = _dataTable.Constraints;
int constraintCount = constraints.Count;
for (int i = 0; i < constraintCount; ++i)
{
if (unique.Equals(constraints[i]))
{
#if DEBUG
if (AdapterSwitches.DataSchema.TraceVerbose)
{
Debug.WriteLine("SetupSchema: duplicate Contraint detected");
}
#endif
unique = null;
break;
}
}
if (null != unique)
{
#if DEBUG
if (AdapterSwitches.DataSchema.TraceVerbose)
{
Debug.WriteLine("SetupSchema: adding new UniqueConstraint");
}
#endif
constraints.Add(unique);
}
}
}
if (!commonFromMultiTable && !string.IsNullOrEmpty(commonBaseTable) && string.IsNullOrEmpty(_dataTable.TableName))
{
_dataTable.TableName = commonBaseTable;
}
if (gettingData)
{
_indexMap = columnIndexMap;
_chapterMap = chapterIndexMap;
dataValues = SetupMapping(schemaRows.Length, columnCollection, chapterColumn, chapterValue);
}
else
{
// debug only, but for retail debug ability
_mappedMode = -1;
}
}
else
{
_dataTable = null;
}
if (addDataRelation)
{
AddRelation(parentChapterColumn, chapterColumn);
}
}
catch (Exception e) when (ADP.IsCatchableOrSecurityExceptionType(e))
{
RollbackAddedItems(addedItems);
throw;
}
return dataValues;
}