SchemaMapping.cs 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. //------------------------------------------------------------------------------
  2. // <copyright file="SchemaMapping.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. //------------------------------------------------------------------------------
  7. namespace System.Data.ProviderBase {
  8. using System.Collections.Generic;
  9. using System.Data;
  10. using System.Data.Common;
  11. using System.Diagnostics;
  12. using System.Globalization;
  13. sealed internal class SchemaMapping {
  14. // DataColumns match in length and name order as the DataReader, no chapters
  15. private const int MapExactMatch = 0;
  16. // DataColumns has different length, but correct name order as the DataReader, no chapters
  17. private const int MapDifferentSize = 1;
  18. // DataColumns may have different length, but a differant name ordering as the DataReader, no chapters
  19. private const int MapReorderedValues = 2;
  20. // DataColumns may have different length, but correct name order as the DataReader, with chapters
  21. private const int MapChapters = 3;
  22. // DataColumns may have different length, but a differant name ordering as the DataReader, with chapters
  23. private const int MapChaptersReordered = 4;
  24. // map xml string data to DataColumn with DataType=typeof(SqlXml)
  25. private const int SqlXml = 1;
  26. // map xml string data to DataColumn with DataType=typeof(XmlDocument)
  27. private const int XmlDocument = 2;
  28. private readonly DataSet _dataSet; // the current dataset, may be null if we are only filling a DataTable
  29. private DataTable _dataTable; // the current DataTable, should never be null
  30. private readonly DataAdapter _adapter;
  31. private readonly DataReaderContainer _dataReader;
  32. private readonly DataTable _schemaTable; // will be null if Fill without schema
  33. private readonly DataTableMapping _tableMapping;
  34. // unique (generated) names based from DataReader.GetName(i)
  35. private readonly string[] _fieldNames;
  36. private readonly object[] _readerDataValues;
  37. private object[] _mappedDataValues; // array passed to dataRow.AddUpdate(), if needed
  38. private int[] _indexMap; // index map that maps dataValues -> _mappedDataValues, if needed
  39. private bool[] _chapterMap; // which DataReader indexes have chapters
  40. private int[] _xmlMap; // map which value in _readerDataValues to convert to a Xml datatype, (SqlXml/XmlDocument)
  41. private int _mappedMode; // modes as described as above
  42. private int _mappedLength;
  43. private readonly LoadOption _loadOption;
  44. internal SchemaMapping(DataAdapter adapter, DataSet dataset, DataTable datatable, DataReaderContainer dataReader, bool keyInfo,
  45. SchemaType schemaType, string sourceTableName, bool gettingData,
  46. DataColumn parentChapterColumn, object parentChapterValue) {
  47. Debug.Assert(null != adapter, "adapter");
  48. Debug.Assert(null != dataReader, "dataReader");
  49. Debug.Assert(0 < dataReader.FieldCount, "FieldCount");
  50. Debug.Assert(null != dataset || null != datatable, "SchemaMapping - null dataSet");
  51. Debug.Assert(SchemaType.Mapped == schemaType || SchemaType.Source == schemaType, "SetupSchema - invalid schemaType");
  52. _dataSet = dataset; // setting DataSet implies chapters are supported
  53. _dataTable = datatable; // setting only DataTable, not DataSet implies chapters are not supported
  54. _adapter = adapter;
  55. _dataReader = dataReader;
  56. if (keyInfo) {
  57. _schemaTable = dataReader.GetSchemaTable();
  58. }
  59. if (adapter.ShouldSerializeFillLoadOption()) {
  60. _loadOption = adapter.FillLoadOption;
  61. }
  62. else if (adapter.AcceptChangesDuringFill) {
  63. _loadOption = (LoadOption)4; // true
  64. }
  65. else {
  66. _loadOption = (LoadOption)5; //false
  67. }
  68. MissingMappingAction mappingAction;
  69. MissingSchemaAction schemaAction;
  70. if (SchemaType.Mapped == schemaType) {
  71. mappingAction = _adapter.MissingMappingAction;
  72. schemaAction = _adapter.MissingSchemaAction;
  73. if (!ADP.IsEmpty(sourceTableName)) { // MDAC 66034
  74. _tableMapping = _adapter.GetTableMappingBySchemaAction(sourceTableName, sourceTableName, mappingAction);
  75. }
  76. else if (null != _dataTable) {
  77. int index = _adapter.IndexOfDataSetTable(_dataTable.TableName);
  78. if (-1 != index) {
  79. _tableMapping = _adapter.TableMappings[index];
  80. }
  81. else {
  82. switch (mappingAction) {
  83. case MissingMappingAction.Passthrough:
  84. _tableMapping = new DataTableMapping(_dataTable.TableName, _dataTable.TableName);
  85. break;
  86. case MissingMappingAction.Ignore:
  87. _tableMapping = null;
  88. break;
  89. case MissingMappingAction.Error:
  90. throw ADP.MissingTableMappingDestination(_dataTable.TableName);
  91. default:
  92. throw ADP.InvalidMissingMappingAction(mappingAction);
  93. }
  94. }
  95. }
  96. }
  97. else if (SchemaType.Source == schemaType) {
  98. mappingAction = System.Data.MissingMappingAction.Passthrough;
  99. schemaAction = Data.MissingSchemaAction.Add;
  100. if (!ADP.IsEmpty(sourceTableName)) { // MDAC 66034
  101. _tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(null, sourceTableName, sourceTableName, mappingAction);
  102. }
  103. else if (null != _dataTable) {
  104. int index = _adapter.IndexOfDataSetTable(_dataTable.TableName); // MDAC 66034
  105. if (-1 != index) {
  106. _tableMapping = _adapter.TableMappings[index];
  107. }
  108. else {
  109. _tableMapping = new DataTableMapping(_dataTable.TableName, _dataTable.TableName);
  110. }
  111. }
  112. }
  113. else {
  114. throw ADP.InvalidSchemaType(schemaType);
  115. }
  116. if (null != _tableMapping) {
  117. if (null == _dataTable) {
  118. _dataTable = _tableMapping.GetDataTableBySchemaAction(_dataSet, schemaAction);
  119. }
  120. if (null != _dataTable) {
  121. _fieldNames = GenerateFieldNames(dataReader);
  122. if (null == _schemaTable) {
  123. _readerDataValues = SetupSchemaWithoutKeyInfo(mappingAction, schemaAction, gettingData, parentChapterColumn, parentChapterValue);
  124. }
  125. else {
  126. _readerDataValues = SetupSchemaWithKeyInfo(mappingAction, schemaAction, gettingData, parentChapterColumn, parentChapterValue);
  127. }
  128. }
  129. // else (null == _dataTable) which means ignore (mapped to nothing)
  130. }
  131. }
  132. internal DataReaderContainer DataReader {
  133. get {
  134. return _dataReader;
  135. }
  136. }
  137. internal DataTable DataTable {
  138. get {
  139. return _dataTable;
  140. }
  141. }
  142. internal object[] DataValues {
  143. get {
  144. return _readerDataValues;
  145. }
  146. }
  147. internal void ApplyToDataRow(DataRow dataRow) {
  148. DataColumnCollection columns = dataRow.Table.Columns;
  149. _dataReader.GetValues(_readerDataValues);
  150. object[] mapped = GetMappedValues();
  151. bool[] readOnly = new bool[mapped.Length];
  152. for (int i = 0; i < readOnly.Length; ++i) {
  153. readOnly[i] = columns[i].ReadOnly;
  154. }
  155. try {
  156. try {
  157. // allow all columns to be written to
  158. for (int i = 0; i < readOnly.Length; ++i) {
  159. if (0 == columns[i].Expression.Length) { // WebData 110773
  160. columns[i].ReadOnly = false;
  161. }
  162. }
  163. for(int i = 0; i < mapped.Length; ++i) {
  164. if (null != mapped[i]) { // MDAC 72659
  165. dataRow[i] = mapped[i];
  166. }
  167. }
  168. }
  169. finally { // ReadOnly
  170. // reset readonly flag on all columns
  171. for (int i = 0; i < readOnly.Length; ++i) {
  172. if (0 == columns[i].Expression.Length) { // WebData 110773
  173. columns[i].ReadOnly = readOnly[i];
  174. }
  175. }
  176. }
  177. }
  178. finally { // FreeDataRowChapters
  179. if (null != _chapterMap) {
  180. FreeDataRowChapters();
  181. }
  182. }
  183. }
  184. private void MappedChapterIndex() { // mode 4
  185. int length = _mappedLength;
  186. for (int i = 0; i < length; i++) {
  187. int k = _indexMap[i];
  188. if (0 <= k) {
  189. _mappedDataValues[k] = _readerDataValues[i]; // from reader to dataset
  190. if (_chapterMap[i]) {
  191. _mappedDataValues[k] = null; // InvalidCast from DataReader to AutoIncrement DataColumn
  192. }
  193. }
  194. }
  195. }
  196. private void MappedChapter() { // mode 3
  197. int length = _mappedLength;
  198. for (int i = 0; i < length; i++) {
  199. _mappedDataValues[i] = _readerDataValues[i]; // from reader to dataset
  200. if (_chapterMap[i]) {
  201. _mappedDataValues[i] = null; // InvalidCast from DataReader to AutoIncrement DataColumn
  202. }
  203. }
  204. }
  205. private void MappedIndex() { // mode 2
  206. Debug.Assert(_mappedLength == _indexMap.Length, "incorrect precomputed length");
  207. int length = _mappedLength;
  208. for (int i = 0; i < length; i++) {
  209. int k = _indexMap[i];
  210. if (0 <= k) {
  211. _mappedDataValues[k] = _readerDataValues[i]; // from reader to dataset
  212. }
  213. }
  214. }
  215. private void MappedValues() { // mode 1
  216. Debug.Assert(_mappedLength == Math.Min(_readerDataValues.Length, _mappedDataValues.Length), "incorrect precomputed length");
  217. int length = _mappedLength;
  218. for (int i = 0; i < length; ++i) {
  219. _mappedDataValues[i] = _readerDataValues[i]; // from reader to dataset
  220. };
  221. }
  222. private object[] GetMappedValues() { // mode 0
  223. if (null != _xmlMap) {
  224. for(int i = 0; i < _xmlMap.Length; ++i) {
  225. if (0 != _xmlMap[i]) {
  226. // get the string/SqlString xml value
  227. string xml = _readerDataValues[i] as string;
  228. if ((null == xml) && (_readerDataValues[i] is System.Data.SqlTypes.SqlString)) {
  229. System.Data.SqlTypes.SqlString x = (System.Data.SqlTypes.SqlString)_readerDataValues[i];
  230. if (!x.IsNull) {
  231. xml = x.Value;
  232. }
  233. else {
  234. switch(_xmlMap[i]) {
  235. case SqlXml:
  236. // map strongly typed SqlString.Null to SqlXml.Null
  237. _readerDataValues[i] = System.Data.SqlTypes.SqlXml.Null;
  238. break;
  239. default:
  240. _readerDataValues[i] = DBNull.Value;
  241. break;
  242. }
  243. }
  244. }
  245. if (null != xml) {
  246. switch(_xmlMap[i]) {
  247. case SqlXml: // turn string into a SqlXml value for DataColumn
  248. System.Xml.XmlReaderSettings settings = new System.Xml.XmlReaderSettings();
  249. settings.ConformanceLevel = System.Xml.ConformanceLevel.Fragment;
  250. System.Xml.XmlReader reader = System.Xml.XmlReader.Create(new System.IO.StringReader(xml), settings, (string)null);
  251. _readerDataValues[i] = new System.Data.SqlTypes.SqlXml(reader);
  252. break;
  253. case XmlDocument: // turn string into XmlDocument value for DataColumn
  254. System.Xml.XmlDocument document = new System.Xml.XmlDocument();
  255. document.LoadXml(xml);
  256. _readerDataValues[i] = document;
  257. break;
  258. }
  259. // default: let value fallthrough to DataSet which may fail with ArgumentException
  260. }
  261. }
  262. }
  263. }
  264. switch(_mappedMode) {
  265. default:
  266. case MapExactMatch:
  267. Debug.Assert(0 == _mappedMode, "incorrect mappedMode");
  268. Debug.Assert((null == _chapterMap) && (null == _indexMap) && (null == _mappedDataValues), "incorrect MappedValues");
  269. return _readerDataValues; // from reader to dataset
  270. case MapDifferentSize:
  271. Debug.Assert((null == _chapterMap) && (null == _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
  272. MappedValues();
  273. break;
  274. case MapReorderedValues:
  275. Debug.Assert((null == _chapterMap) && (null != _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
  276. MappedIndex();
  277. break;
  278. case MapChapters:
  279. Debug.Assert((null != _chapterMap) && (null == _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
  280. MappedChapter();
  281. break;
  282. case MapChaptersReordered:
  283. Debug.Assert((null != _chapterMap) && (null != _indexMap) && (null != _mappedDataValues), "incorrect MappedValues");
  284. MappedChapterIndex();
  285. break;
  286. }
  287. return _mappedDataValues;
  288. }
  289. internal void LoadDataRowWithClear() {
  290. // for FillErrorEvent to ensure no values leftover from previous row
  291. for (int i = 0; i < _readerDataValues.Length; ++i) {
  292. _readerDataValues[i] = null;
  293. }
  294. LoadDataRow();
  295. }
  296. internal void LoadDataRow() {
  297. try {
  298. _dataReader.GetValues(_readerDataValues);
  299. object[] mapped = GetMappedValues();
  300. DataRow dataRow;
  301. switch(_loadOption) {
  302. case LoadOption.OverwriteChanges:
  303. case LoadOption.PreserveChanges:
  304. case LoadOption.Upsert:
  305. dataRow = _dataTable.LoadDataRow(mapped, _loadOption);
  306. break;
  307. case (LoadOption)4: // true
  308. dataRow = _dataTable.LoadDataRow(mapped, true);
  309. break;
  310. case (LoadOption)5: // false
  311. dataRow = _dataTable.LoadDataRow(mapped, false);
  312. break;
  313. default:
  314. Debug.Assert(false, "unexpected LoadOption");
  315. throw ADP.InvalidLoadOption(_loadOption);
  316. }
  317. if ((null != _chapterMap) && (null != _dataSet)) {
  318. LoadDataRowChapters(dataRow); // MDAC 70772
  319. }
  320. }
  321. finally {
  322. if (null != _chapterMap) {
  323. FreeDataRowChapters(); // MDAC 71900
  324. }
  325. }
  326. }
  327. private void FreeDataRowChapters() {
  328. for(int i = 0; i < _chapterMap.Length; ++i) {
  329. if (_chapterMap[i]) {
  330. IDisposable disposable = (_readerDataValues[i] as IDisposable);
  331. if (null != disposable) {
  332. _readerDataValues[i] = null;
  333. disposable.Dispose();
  334. }
  335. }
  336. }
  337. }
  338. internal int LoadDataRowChapters(DataRow dataRow) {
  339. int datarowadded = 0;
  340. int rowLength = _chapterMap.Length;
  341. for(int i = 0; i < rowLength; ++i) {
  342. if (_chapterMap[i]) {
  343. object readerValue = _readerDataValues[i];
  344. if ((null != readerValue) && !Convert.IsDBNull(readerValue)) { // MDAC 70441
  345. _readerDataValues[i] = null;
  346. using (IDataReader nestedReader = (IDataReader) readerValue) {
  347. if (!nestedReader.IsClosed) {
  348. Debug.Assert(null != _dataSet, "if chapters, then Fill(DataSet,...) not Fill(DataTable,...)");
  349. object parentChapterValue;
  350. DataColumn parentChapterColumn;
  351. if (null == _indexMap) {
  352. parentChapterColumn = _dataTable.Columns[i];
  353. parentChapterValue = dataRow[parentChapterColumn];
  354. }
  355. else {
  356. parentChapterColumn = _dataTable.Columns[_indexMap[i]];
  357. parentChapterValue = dataRow[parentChapterColumn];
  358. }
  359. // correct on Fill, not FillFromReader
  360. string chapterTableName = _tableMapping.SourceTable + _fieldNames[i]; // MDAC 70908
  361. DataReaderContainer readerHandler = DataReaderContainer.Create(nestedReader, _dataReader.ReturnProviderSpecificTypes);
  362. datarowadded += _adapter.FillFromReader(_dataSet, null, chapterTableName, readerHandler, 0, 0, parentChapterColumn, parentChapterValue);
  363. }
  364. }
  365. }
  366. }
  367. }
  368. return datarowadded;
  369. }
  370. private int[] CreateIndexMap(int count, int index) {
  371. int[] values = new int[count];
  372. for (int i = 0; i < index; ++i) {
  373. values[i] = i;
  374. }
  375. return values;
  376. }
  377. private static string[] GenerateFieldNames(DataReaderContainer dataReader) {
  378. string[] fieldNames = new string[dataReader.FieldCount];
  379. for(int i = 0; i < fieldNames.Length; ++i) {
  380. fieldNames[i] = dataReader.GetName(i);
  381. }
  382. ADP.BuildSchemaTableInfoTableNames(fieldNames);
  383. return fieldNames;
  384. }
  385. private DataColumn[] ResizeColumnArray(DataColumn[] rgcol, int len) {
  386. Debug.Assert(rgcol != null, "invalid call to ResizeArray");
  387. Debug.Assert(len <= rgcol.Length, "invalid len passed to ResizeArray");
  388. DataColumn[] tmp = new DataColumn[len];
  389. Array.Copy(rgcol, tmp, len);
  390. return tmp;
  391. }
  392. private void AddItemToAllowRollback(ref List<object> items, object value) {
  393. if (null == items) {
  394. items = new List<object>();
  395. }
  396. items.Add(value);
  397. }
  398. private void RollbackAddedItems(List<object> items) {
  399. if (null != items) {
  400. for (int i = items.Count-1; 0 <= i; --i) {
  401. // remove columns that were added now that we are failing
  402. if (null != items[i]) {
  403. DataColumn column = (items[i] as DataColumn);
  404. if (null != column) {
  405. if (null != column.Table) {
  406. column.Table.Columns.Remove(column);
  407. }
  408. }
  409. else {
  410. DataTable table = (items[i] as DataTable);
  411. if (null != table) {
  412. if (null != table.DataSet) {
  413. table.DataSet.Tables.Remove(table);
  414. }
  415. }
  416. }
  417. }
  418. }
  419. }
  420. }
  421. private object[] SetupSchemaWithoutKeyInfo(MissingMappingAction mappingAction, MissingSchemaAction schemaAction, bool gettingData, DataColumn parentChapterColumn, object chapterValue) {
  422. int[] columnIndexMap = null;
  423. bool[] chapterIndexMap = null;
  424. int mappingCount = 0;
  425. int count = _dataReader.FieldCount;
  426. object[] dataValues = null;
  427. List<object> addedItems = null;
  428. try {
  429. DataColumnCollection columnCollection = _dataTable.Columns;
  430. columnCollection.EnsureAdditionalCapacity(count + (chapterValue != null ? 1 : 0));
  431. // We can always just create column if there are no existing column or column mappings, and the mapping action is passthrough
  432. bool alwaysCreateColumns = ((_dataTable.Columns.Count == 0) && ((_tableMapping.ColumnMappings == null) || (_tableMapping.ColumnMappings.Count == 0)) && (mappingAction == MissingMappingAction.Passthrough));
  433. for (int i = 0; i < count; ++i) {
  434. bool ischapter = false;
  435. Type fieldType = _dataReader.GetFieldType(i);
  436. if (null == fieldType) {
  437. throw ADP.MissingDataReaderFieldType(i);
  438. }
  439. // if IDataReader, hierarchy exists and we will use an Int32,AutoIncrementColumn in this table
  440. if (typeof(IDataReader).IsAssignableFrom(fieldType)) {
  441. if (null == chapterIndexMap) {
  442. chapterIndexMap = new bool[count];
  443. }
  444. chapterIndexMap[i] = ischapter = true;
  445. fieldType = typeof(Int32);
  446. }
  447. else if (typeof(System.Data.SqlTypes.SqlXml).IsAssignableFrom(fieldType)) {
  448. if (null == _xmlMap) { // map to DataColumn with DataType=typeof(SqlXml)
  449. _xmlMap = new int[count];
  450. }
  451. _xmlMap[i] = SqlXml; // track its xml data
  452. }
  453. else if (typeof(System.Xml.XmlReader).IsAssignableFrom(fieldType)) {
  454. fieldType = typeof(String); // map to DataColumn with DataType=typeof(string)
  455. if (null == _xmlMap) {
  456. _xmlMap = new int[count];
  457. }
  458. _xmlMap[i] = XmlDocument; // track its xml data
  459. }
  460. DataColumn dataColumn;
  461. if (alwaysCreateColumns) {
  462. dataColumn = DataColumnMapping.CreateDataColumnBySchemaAction(_fieldNames[i], _fieldNames[i], _dataTable, fieldType, schemaAction);
  463. }
  464. else {
  465. dataColumn = _tableMapping.GetDataColumn(_fieldNames[i], fieldType, _dataTable, mappingAction, schemaAction);
  466. }
  467. if (null == dataColumn) {
  468. if (null == columnIndexMap) {
  469. columnIndexMap = CreateIndexMap(count, i);
  470. }
  471. columnIndexMap[i] = -1;
  472. continue; // null means ignore (mapped to nothing)
  473. }
  474. else if ((null != _xmlMap) && (0 != _xmlMap[i])) {
  475. if (typeof(System.Data.SqlTypes.SqlXml) == dataColumn.DataType) {
  476. _xmlMap[i] = SqlXml;
  477. }
  478. else if (typeof(System.Xml.XmlDocument) == dataColumn.DataType) {
  479. _xmlMap[i] = XmlDocument;
  480. }
  481. else {
  482. _xmlMap[i] = 0; // datacolumn is not a specific Xml dataType, i.e. string
  483. int total = 0;
  484. for(int x = 0; x < _xmlMap.Length; ++x) {
  485. total += _xmlMap[x];
  486. }
  487. if (0 == total) { // not mapping to a specific Xml datatype, get rid of the map
  488. _xmlMap = null;
  489. }
  490. }
  491. }
  492. if (null == dataColumn.Table) {
  493. if (ischapter) {
  494. dataColumn.AllowDBNull = false;
  495. dataColumn.AutoIncrement = true;
  496. dataColumn.ReadOnly = true;
  497. }
  498. AddItemToAllowRollback(ref addedItems, dataColumn);
  499. columnCollection.Add(dataColumn);
  500. }
  501. else if (ischapter && !dataColumn.AutoIncrement) {
  502. throw ADP.FillChapterAutoIncrement();
  503. }
  504. if (null != columnIndexMap) {
  505. columnIndexMap[i] = dataColumn.Ordinal;
  506. }
  507. else if (i != dataColumn.Ordinal) {
  508. columnIndexMap = CreateIndexMap(count, i);
  509. columnIndexMap[i] = dataColumn.Ordinal;
  510. }
  511. // else i == dataColumn.Ordinal and columnIndexMap can be optimized out
  512. mappingCount++;
  513. }
  514. bool addDataRelation = false;
  515. DataColumn chapterColumn = null;
  516. if (null != chapterValue) { // add the extra column in the child table
  517. Type fieldType = chapterValue.GetType();
  518. chapterColumn = _tableMapping.GetDataColumn(_tableMapping.SourceTable, fieldType, _dataTable, mappingAction, schemaAction);
  519. if (null != chapterColumn) {
  520. if (null == chapterColumn.Table) {
  521. AddItemToAllowRollback(ref addedItems, chapterColumn);
  522. columnCollection.Add(chapterColumn);
  523. addDataRelation = (null != parentChapterColumn);
  524. }
  525. mappingCount++;
  526. }
  527. }
  528. if (0 < mappingCount) {
  529. if ((null != _dataSet) && (null == _dataTable.DataSet)) {
  530. // Allowed to throw exception if DataTable is from wrong DataSet
  531. AddItemToAllowRollback(ref addedItems, _dataTable);
  532. _dataSet.Tables.Add(_dataTable);
  533. }
  534. if (gettingData) {
  535. if (null == columnCollection) {
  536. columnCollection = _dataTable.Columns;
  537. }
  538. _indexMap = columnIndexMap;
  539. _chapterMap = chapterIndexMap;
  540. dataValues = SetupMapping(count, columnCollection, chapterColumn, chapterValue);
  541. }
  542. else {
  543. // debug only, but for retail debug ability
  544. _mappedMode = -1;
  545. }
  546. }
  547. else {
  548. _dataTable = null;
  549. }
  550. if (addDataRelation) {
  551. AddRelation(parentChapterColumn, chapterColumn);
  552. }
  553. }
  554. catch (Exception e) {
  555. //
  556. if (ADP.IsCatchableOrSecurityExceptionType(e)) {
  557. RollbackAddedItems(addedItems);
  558. }
  559. throw;
  560. }
  561. return dataValues;
  562. }
  563. private object[] SetupSchemaWithKeyInfo(MissingMappingAction mappingAction, MissingSchemaAction schemaAction, bool gettingData, DataColumn parentChapterColumn, object chapterValue) {
  564. // must sort rows from schema table by ordinal because Jet is sorted by coumn name
  565. DbSchemaRow[] schemaRows = DbSchemaRow.GetSortedSchemaRows(_schemaTable, _dataReader.ReturnProviderSpecificTypes); // MDAC 60609
  566. Debug.Assert(null != schemaRows, "SchemaSetup - null DbSchemaRow[]");
  567. Debug.Assert(_dataReader.FieldCount <= schemaRows.Length, "unexpected fewer rows in Schema than FieldCount");
  568. if (0 == schemaRows.Length) {
  569. _dataTable = null;
  570. return (object[])null;
  571. }
  572. // Everett behavior, always add a primary key if a primary key didn't exist before
  573. // Whidbey behavior, same as Everett unless using LoadOption then add primary key only if no columns previously existed
  574. bool addPrimaryKeys = (((0 == _dataTable.PrimaryKey.Length) && ((4 <= (int)_loadOption) || (0 == _dataTable.Rows.Count)))
  575. || (0 == _dataTable.Columns.Count)); // MDAC 67033
  576. DataColumn[] keys = null;
  577. int keyCount = 0;
  578. bool isPrimary = true; // assume key info (if any) is about a primary key
  579. string keyBaseTable = null;
  580. string commonBaseTable = null;
  581. bool keyFromMultiTable = false;
  582. bool commonFromMultiTable = false;
  583. int[] columnIndexMap = null;
  584. bool[] chapterIndexMap = null;
  585. int mappingCount = 0;
  586. object[] dataValues = null;
  587. List<object> addedItems = null;
  588. DataColumnCollection columnCollection = _dataTable.Columns;
  589. try {
  590. for(int sortedIndex = 0; sortedIndex < schemaRows.Length; ++sortedIndex) {
  591. DbSchemaRow schemaRow = schemaRows[sortedIndex];
  592. int unsortedIndex = schemaRow.UnsortedIndex; // MDAC 67050
  593. bool ischapter = false;
  594. Type fieldType = schemaRow.DataType;
  595. if (null == fieldType) {
  596. fieldType = _dataReader.GetFieldType(sortedIndex);
  597. }
  598. if (null == fieldType) {
  599. throw ADP.MissingDataReaderFieldType(sortedIndex);
  600. }
  601. // if IDataReader, hierarchy exists and we will use an Int32,AutoIncrementColumn in this table
  602. if (typeof(IDataReader).IsAssignableFrom(fieldType)) {
  603. if (null == chapterIndexMap) {
  604. chapterIndexMap = new bool[schemaRows.Length];
  605. }
  606. chapterIndexMap[unsortedIndex] = ischapter = true;
  607. fieldType = typeof(Int32);
  608. }
  609. else if (typeof(System.Data.SqlTypes.SqlXml).IsAssignableFrom(fieldType)) {
  610. if (null == _xmlMap) {
  611. _xmlMap = new int[schemaRows.Length];
  612. }
  613. _xmlMap[sortedIndex] = SqlXml;
  614. }
  615. else if (typeof(System.Xml.XmlReader).IsAssignableFrom(fieldType)) {
  616. fieldType = typeof(String);
  617. if (null == _xmlMap) {
  618. _xmlMap = new int[schemaRows.Length];
  619. }
  620. _xmlMap[sortedIndex] = XmlDocument;
  621. }
  622. DataColumn dataColumn = null;
  623. if (!schemaRow.IsHidden ) {
  624. dataColumn = _tableMapping.GetDataColumn(_fieldNames[sortedIndex], fieldType, _dataTable, mappingAction, schemaAction);
  625. }
  626. string basetable = /*schemaRow.BaseServerName+schemaRow.BaseCatalogName+schemaRow.BaseSchemaName+*/ schemaRow.BaseTableName;
  627. if (null == dataColumn) {
  628. if (null == columnIndexMap) {
  629. columnIndexMap = CreateIndexMap(schemaRows.Length, unsortedIndex);
  630. }
  631. columnIndexMap[unsortedIndex] = -1;
  632. // if the column is not mapped and it is a key, then don't add any key information
  633. if (schemaRow.IsKey) { // MDAC 90822
  634. #if DEBUG
  635. if (AdapterSwitches.DataSchema.TraceVerbose) {
  636. Debug.WriteLine("SetupSchema: partial primary key detected");
  637. }
  638. #endif
  639. // if the hidden key comes from a different table - don't throw away the primary key
  640. // example SELECT [T2].[ID], [T2].[ProdID], [T2].[VendorName] FROM [Vendor] AS [T2], [Prod] AS [T1] WHERE (([T1].[ProdID] = [T2].[ProdID]))
  641. if (keyFromMultiTable || (schemaRow.BaseTableName == keyBaseTable)) { // WebData 100376
  642. addPrimaryKeys = false; // don't add any future keys now
  643. keys = null; // get rid of any keys we've seen
  644. }
  645. }
  646. continue; // null means ignore (mapped to nothing)
  647. }
  648. else if ((null != _xmlMap) && (0 != _xmlMap[sortedIndex])) {
  649. if (typeof(System.Data.SqlTypes.SqlXml) == dataColumn.DataType) {
  650. _xmlMap[sortedIndex] = SqlXml;
  651. }
  652. else if (typeof(System.Xml.XmlDocument) == dataColumn.DataType) {
  653. _xmlMap[sortedIndex] = XmlDocument;
  654. }
  655. else {
  656. _xmlMap[sortedIndex] = 0; // datacolumn is not a specific Xml dataType, i.e. string
  657. int total = 0;
  658. for(int x = 0; x < _xmlMap.Length; ++x) {
  659. total += _xmlMap[x];
  660. }
  661. if (0 == total) { // not mapping to a specific Xml datatype, get rid of the map
  662. _xmlMap = null;
  663. }
  664. }
  665. }
  666. if (schemaRow.IsKey) {
  667. if (basetable != keyBaseTable) {
  668. if (null == keyBaseTable) {
  669. keyBaseTable = basetable;
  670. }
  671. else keyFromMultiTable = true;
  672. }
  673. }
  674. if (ischapter) {
  675. if (null == dataColumn.Table) {
  676. dataColumn.AllowDBNull = false;
  677. dataColumn.AutoIncrement = true;
  678. dataColumn.ReadOnly = true;
  679. }
  680. else if (!dataColumn.AutoIncrement) {
  681. throw ADP.FillChapterAutoIncrement();
  682. }
  683. }
  684. else {// MDAC 67033
  685. if (!commonFromMultiTable) {
  686. if ((basetable != commonBaseTable) && (!ADP.IsEmpty(basetable))) {
  687. if (null == commonBaseTable) {
  688. commonBaseTable = basetable;
  689. }
  690. else {
  691. commonFromMultiTable = true;
  692. }
  693. }
  694. }
  695. if (4 <= (int)_loadOption) {
  696. if (schemaRow.IsAutoIncrement && DataColumn.IsAutoIncrementType(fieldType)) {
  697. //
  698. dataColumn.AutoIncrement = true;
  699. if (!schemaRow.AllowDBNull) { // MDAC 71060
  700. dataColumn.AllowDBNull = false;
  701. }
  702. }
  703. // setup maxLength, only for string columns since this is all the DataSet supports
  704. if (fieldType == typeof(string)) {
  705. //@devnote: schemaRow.Size is count of characters for string columns, count of bytes otherwise
  706. dataColumn.MaxLength = schemaRow.Size>0?schemaRow.Size:-1;
  707. }
  708. if (schemaRow.IsReadOnly) {
  709. dataColumn.ReadOnly = true;
  710. }
  711. if (!schemaRow.AllowDBNull && (!schemaRow.IsReadOnly || schemaRow.IsKey)) { // MDAC 71060, 72252
  712. dataColumn.AllowDBNull = false;
  713. }
  714. if (schemaRow.IsUnique && !schemaRow.IsKey && !fieldType.IsArray) {
  715. // note, arrays are not comparable so only mark non-arrays as unique, ie timestamp columns
  716. // are unique, but not comparable
  717. dataColumn.Unique = true;
  718. if (!schemaRow.AllowDBNull) { // MDAC 71060
  719. dataColumn.AllowDBNull = false;
  720. }
  721. }
  722. }
  723. else if (null == dataColumn.Table) {
  724. dataColumn.AutoIncrement = schemaRow.IsAutoIncrement;
  725. dataColumn.AllowDBNull = schemaRow.AllowDBNull;
  726. dataColumn.ReadOnly = schemaRow.IsReadOnly;
  727. dataColumn.Unique = schemaRow.IsUnique;
  728. if (fieldType == typeof(string) || (fieldType == typeof(SqlTypes.SqlString))) {
  729. //@devnote: schemaRow.Size is count of characters for string columns, count of bytes otherwise
  730. dataColumn.MaxLength = schemaRow.Size;
  731. }
  732. }
  733. }
  734. if (null == dataColumn.Table) {
  735. if (4 > (int)_loadOption) {
  736. AddAdditionalProperties(dataColumn, schemaRow.DataRow);
  737. }
  738. AddItemToAllowRollback(ref addedItems, dataColumn);
  739. columnCollection.Add(dataColumn);
  740. }
  741. // The server sends us one key per table according to these rules.
  742. //
  743. // 1. If the table has a primary key, the server sends us this key.
  744. // 2. If the table has a primary key and a unique key, it sends us the primary key
  745. // 3. if the table has no primary key but has a unique key, it sends us the unique key
  746. //
  747. // In case 3, we will promote a unique key to a primary key IFF all the columns that compose
  748. // that key are not nullable since no columns in a primary key can be null. If one or more
  749. // of the keys is nullable, then we will add a unique constraint.
  750. //
  751. if (addPrimaryKeys && schemaRow.IsKey) { // MDAC 67033
  752. if (keys == null) {
  753. keys = new DataColumn[schemaRows.Length];
  754. }
  755. keys[keyCount++] = dataColumn;
  756. #if DEBUG
  757. if (AdapterSwitches.DataSchema.TraceVerbose) {
  758. Debug.WriteLine("SetupSchema: building list of " + ((isPrimary) ? "PrimaryKey" : "UniqueConstraint"));
  759. }
  760. #endif
  761. // see case 3 above, we do want dataColumn.AllowDBNull not schemaRow.AllowDBNull
  762. // otherwise adding PrimaryKey will change AllowDBNull to false
  763. if (isPrimary && dataColumn.AllowDBNull) { // MDAC 72241
  764. #if DEBUG
  765. if (AdapterSwitches.DataSchema.TraceVerbose) {
  766. Debug.WriteLine("SetupSchema: changing PrimaryKey into UniqueContraint");
  767. }
  768. #endif
  769. isPrimary = false;
  770. }
  771. }
  772. if (null != columnIndexMap) {
  773. columnIndexMap[unsortedIndex] = dataColumn.Ordinal;
  774. }
  775. else if (unsortedIndex != dataColumn.Ordinal) {
  776. columnIndexMap = CreateIndexMap(schemaRows.Length, unsortedIndex);
  777. columnIndexMap[unsortedIndex] = dataColumn.Ordinal;
  778. }
  779. mappingCount++;
  780. }
  781. bool addDataRelation = false;
  782. DataColumn chapterColumn = null;
  783. if (null != chapterValue) { // add the extra column in the child table
  784. Type fieldType = chapterValue.GetType();
  785. chapterColumn = _tableMapping.GetDataColumn(_tableMapping.SourceTable, fieldType, _dataTable, mappingAction, schemaAction);
  786. if (null != chapterColumn) {
  787. if (null == chapterColumn.Table) {
  788. chapterColumn.ReadOnly = true; // MDAC 71878
  789. chapterColumn.AllowDBNull = false;
  790. AddItemToAllowRollback(ref addedItems, chapterColumn);
  791. columnCollection.Add(chapterColumn);
  792. addDataRelation = (null != parentChapterColumn);
  793. }
  794. mappingCount++;
  795. }
  796. }
  797. if (0 < mappingCount) {
  798. if ((null != _dataSet) && null == _dataTable.DataSet) {
  799. AddItemToAllowRollback(ref addedItems, _dataTable);
  800. _dataSet.Tables.Add(_dataTable);
  801. }
  802. // setup the key
  803. if (addPrimaryKeys && (null != keys)) { // MDAC 67033
  804. if (keyCount < keys.Length) {
  805. keys = ResizeColumnArray(keys, keyCount);
  806. }
  807. // MDAC 66188
  808. if (isPrimary) {
  809. #if DEBUG
  810. if (AdapterSwitches.DataSchema.TraceVerbose) {
  811. Debug.WriteLine("SetupSchema: set_PrimaryKey");
  812. }
  813. #endif
  814. _dataTable.PrimaryKey = keys;
  815. }
  816. else {
  817. UniqueConstraint unique = new UniqueConstraint("", keys);
  818. ConstraintCollection constraints = _dataTable.Constraints;
  819. int constraintCount = constraints.Count;
  820. for (int i = 0; i < constraintCount; ++i) {
  821. if (unique.Equals(constraints[i])) {
  822. #if DEBUG
  823. if (AdapterSwitches.DataSchema.TraceVerbose) {
  824. Debug.WriteLine("SetupSchema: duplicate Contraint detected");
  825. }
  826. #endif
  827. unique = null;
  828. break;
  829. }
  830. }
  831. if (null != unique) {
  832. #if DEBUG
  833. if (AdapterSwitches.DataSchema.TraceVerbose) {
  834. Debug.WriteLine("SetupSchema: adding new UniqueConstraint");
  835. }
  836. #endif
  837. constraints.Add(unique);
  838. }
  839. }
  840. }
  841. if (!commonFromMultiTable && !ADP.IsEmpty(commonBaseTable) && ADP.IsEmpty(_dataTable.TableName)) {
  842. _dataTable.TableName = commonBaseTable;
  843. }
  844. if (gettingData) {
  845. _indexMap = columnIndexMap;
  846. _chapterMap = chapterIndexMap;
  847. dataValues = SetupMapping(schemaRows.Length, columnCollection, chapterColumn, chapterValue);
  848. }
  849. else {
  850. // debug only, but for retail debug ability
  851. _mappedMode = -1;
  852. }
  853. }
  854. else {
  855. _dataTable = null;
  856. }
  857. if (addDataRelation) {
  858. AddRelation(parentChapterColumn, chapterColumn);
  859. }
  860. }
  861. catch (Exception e) {
  862. if (ADP.IsCatchableOrSecurityExceptionType(e)) {
  863. RollbackAddedItems(addedItems);
  864. }
  865. throw;
  866. }
  867. return dataValues;
  868. }
  869. private void AddAdditionalProperties(DataColumn targetColumn, DataRow schemaRow) {
  870. DataColumnCollection columns = schemaRow.Table.Columns;
  871. DataColumn column;
  872. column = columns[SchemaTableOptionalColumn.DefaultValue];
  873. if (null != column) {
  874. targetColumn.DefaultValue = schemaRow[column];
  875. }
  876. column = columns[SchemaTableOptionalColumn.AutoIncrementSeed];
  877. if (null != column) {
  878. object value = schemaRow[column];
  879. if (DBNull.Value != value) {
  880. targetColumn.AutoIncrementSeed = ((IConvertible)value).ToInt64(CultureInfo.InvariantCulture);
  881. }
  882. }
  883. column = columns[SchemaTableOptionalColumn.AutoIncrementStep];
  884. if (null != column) {
  885. object value = schemaRow[column];
  886. if (DBNull.Value != value) {
  887. targetColumn.AutoIncrementStep = ((IConvertible)value).ToInt64(CultureInfo.InvariantCulture);
  888. }
  889. }
  890. column = columns[SchemaTableOptionalColumn.ColumnMapping];
  891. if (null != column) {
  892. object value = schemaRow[column];
  893. if (DBNull.Value != value) {
  894. targetColumn.ColumnMapping = (MappingType)((IConvertible)value).ToInt32(CultureInfo.InvariantCulture);
  895. }
  896. }
  897. column = columns[SchemaTableOptionalColumn.BaseColumnNamespace];
  898. if (null != column) {
  899. object value = schemaRow[column];
  900. if (DBNull.Value != value) {
  901. targetColumn.Namespace = ((IConvertible)value).ToString(CultureInfo.InvariantCulture);
  902. }
  903. }
  904. column = columns[SchemaTableOptionalColumn.Expression];
  905. if (null != column) {
  906. object value = schemaRow[column];
  907. if (DBNull.Value != value) {
  908. targetColumn.Expression = ((IConvertible)value).ToString(CultureInfo.InvariantCulture);
  909. }
  910. }
  911. }
  912. private void AddRelation(DataColumn parentChapterColumn, DataColumn chapterColumn) { // MDAC 71613
  913. if (null != _dataSet) {
  914. string name = /*parentChapterColumn.ColumnName + "_" +*/ chapterColumn.ColumnName; // MDAC 72815
  915. DataRelation relation = new DataRelation(name, new DataColumn[] { parentChapterColumn }, new DataColumn[] { chapterColumn }, false); // MDAC 71878
  916. int index = 1;
  917. string tmp = name;
  918. DataRelationCollection relations = _dataSet.Relations;
  919. while (-1 != relations.IndexOf(tmp)) {
  920. tmp = name + index;
  921. index++;
  922. }
  923. relation.RelationName = tmp;
  924. relations.Add(relation);
  925. }
  926. }
  927. private object[] SetupMapping(int count, DataColumnCollection columnCollection, DataColumn chapterColumn, object chapterValue) {
  928. object[] dataValues = new object[count];
  929. if (null == _indexMap) {
  930. int mappingCount = columnCollection.Count;
  931. bool hasChapters = (null != _chapterMap);
  932. if ((count != mappingCount) || hasChapters) {
  933. _mappedDataValues = new object[mappingCount];
  934. if (hasChapters) {
  935. _mappedMode = MapChapters;
  936. _mappedLength = count;
  937. }
  938. else {
  939. _mappedMode = MapDifferentSize;
  940. _mappedLength = Math.Min(count, mappingCount);
  941. }
  942. }
  943. else {
  944. _mappedMode = MapExactMatch; /* _mappedLength doesn't matter */
  945. }
  946. }
  947. else {
  948. _mappedDataValues = new object[columnCollection.Count];
  949. _mappedMode = ((null == _chapterMap) ? MapReorderedValues : MapChaptersReordered);
  950. _mappedLength = count;
  951. }
  952. if (null != chapterColumn) { // value from parent tracked into child table
  953. _mappedDataValues[chapterColumn.Ordinal] = chapterValue;
  954. }
  955. return dataValues;
  956. }
  957. }
  958. }