DbMetaDataFactory.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. //------------------------------------------------------------------------------
  2. // <copyright file="dbmetadatafactory.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.ProviderBase {
  9. using System;
  10. using System.Collections;
  11. using System.Data;
  12. using System.Data.Common;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.IO;
  16. using System.Xml;
  17. using System.Xml.Schema;
  18. internal class DbMetaDataFactory{ // V1.2.3300
  19. private DataSet _metaDataCollectionsDataSet;
  20. private string _normalizedServerVersion;
  21. private string _serverVersionString;
  22. // well known column names
  23. private const string _collectionName = "CollectionName";
  24. private const string _populationMechanism = "PopulationMechanism";
  25. private const string _populationString = "PopulationString";
  26. private const string _maximumVersion = "MaximumVersion";
  27. private const string _minimumVersion = "MinimumVersion";
  28. private const string _dataSourceProductVersionNormalized = "DataSourceProductVersionNormalized";
  29. private const string _dataSourceProductVersion = "DataSourceProductVersion";
  30. private const string _restrictionDefault = "RestrictionDefault";
  31. private const string _restrictionNumber = "RestrictionNumber";
  32. private const string _numberOfRestrictions = "NumberOfRestrictions";
  33. private const string _restrictionName = "RestrictionName";
  34. private const string _parameterName = "ParameterName";
  35. // population mechanisms
  36. private const string _dataTable = "DataTable";
  37. private const string _sqlCommand = "SQLCommand";
  38. private const string _prepareCollection = "PrepareCollection";
  39. public DbMetaDataFactory(Stream xmlStream, string serverVersion, string normalizedServerVersion) {
  40. ADP.CheckArgumentNull(xmlStream, "xmlStream");
  41. ADP.CheckArgumentNull(serverVersion, "serverVersion");
  42. ADP.CheckArgumentNull(normalizedServerVersion, "normalizedServerVersion");
  43. LoadDataSetFromXml(xmlStream);
  44. _serverVersionString = serverVersion;
  45. _normalizedServerVersion = normalizedServerVersion;
  46. }
  47. protected DataSet CollectionDataSet {
  48. get {
  49. return _metaDataCollectionsDataSet;
  50. }
  51. }
  52. protected string ServerVersion {
  53. get {
  54. return _serverVersionString;
  55. }
  56. }
  57. protected string ServerVersionNormalized {
  58. get {
  59. return _normalizedServerVersion;
  60. }
  61. }
  62. protected DataTable CloneAndFilterCollection(string collectionName, string[] hiddenColumnNames) {
  63. DataTable sourceTable;
  64. DataTable destinationTable;
  65. DataColumn[] filteredSourceColumns;
  66. DataColumnCollection destinationColumns;
  67. DataRow newRow;
  68. sourceTable = _metaDataCollectionsDataSet.Tables[collectionName];
  69. if ((sourceTable == null) || (collectionName != sourceTable.TableName)) {
  70. throw ADP.DataTableDoesNotExist(collectionName);
  71. }
  72. destinationTable = new DataTable(collectionName);
  73. destinationTable.Locale = CultureInfo.InvariantCulture;
  74. destinationColumns = destinationTable.Columns;
  75. filteredSourceColumns = FilterColumns(sourceTable,hiddenColumnNames,destinationColumns);
  76. foreach (DataRow row in sourceTable.Rows) {
  77. if (SupportedByCurrentVersion(row) == true) {
  78. newRow = destinationTable.NewRow();
  79. for(int i = 0; i < destinationColumns.Count; i++) {
  80. newRow[destinationColumns[i]] = row[filteredSourceColumns[i],DataRowVersion.Current];
  81. }
  82. destinationTable.Rows.Add(newRow);
  83. newRow.AcceptChanges();
  84. }
  85. }
  86. return destinationTable;
  87. }
  88. public void Dispose() {
  89. Dispose(true);
  90. }
  91. virtual protected void Dispose(bool disposing) {
  92. if (disposing) {
  93. _normalizedServerVersion = null;
  94. _serverVersionString = null;
  95. _metaDataCollectionsDataSet.Dispose();
  96. }
  97. }
  98. private DataTable ExecuteCommand(DataRow requestedCollectionRow, String[] restrictions, DbConnection connection){
  99. DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
  100. DataColumn populationStringColumn = metaDataCollectionsTable.Columns[_populationString];
  101. DataColumn numberOfRestrictionsColumn = metaDataCollectionsTable.Columns[_numberOfRestrictions];
  102. DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[_collectionName];
  103. //DataColumn restrictionNameColumn = metaDataCollectionsTable.Columns[_restrictionName];
  104. DataTable resultTable = null;
  105. DbCommand command = null;
  106. DataTable schemaTable = null;
  107. Debug.Assert(requestedCollectionRow != null);
  108. String sqlCommand = requestedCollectionRow[populationStringColumn,DataRowVersion.Current] as string;
  109. int numberOfRestrictions = (int)requestedCollectionRow[numberOfRestrictionsColumn,DataRowVersion.Current] ;
  110. String collectionName = requestedCollectionRow[collectionNameColumn,DataRowVersion.Current] as string;
  111. if ((restrictions != null) && (restrictions.Length > numberOfRestrictions)) {
  112. throw ADP.TooManyRestrictions(collectionName);
  113. }
  114. command = connection.CreateCommand();
  115. command.CommandText = sqlCommand;
  116. command.CommandTimeout = System.Math.Max(command.CommandTimeout,180);
  117. for (int i = 0; i < numberOfRestrictions; i++) {
  118. DbParameter restrictionParameter = command.CreateParameter();
  119. if ((restrictions != null) && (restrictions.Length > i ) && (restrictions[i] != null)) {
  120. restrictionParameter.Value = restrictions[i];
  121. }
  122. else {
  123. // This is where we have to assign null to the value of the parameter.
  124. restrictionParameter.Value = DBNull.Value;
  125. }
  126. restrictionParameter.ParameterName = GetParameterName(collectionName, i+1);
  127. restrictionParameter.Direction = ParameterDirection.Input;
  128. command.Parameters.Add(restrictionParameter);
  129. }
  130. DbDataReader reader = null;
  131. try {
  132. try {
  133. reader = command.ExecuteReader();
  134. }
  135. catch (Exception e) {
  136. if (!ADP.IsCatchableExceptionType(e)) {
  137. throw;
  138. }
  139. throw ADP.QueryFailed(collectionName,e);
  140. }
  141. //
  142. // Build a DataTable from the reader
  143. resultTable = new DataTable(collectionName);
  144. resultTable.Locale = CultureInfo.InvariantCulture;
  145. schemaTable = reader.GetSchemaTable();
  146. foreach (DataRow row in schemaTable.Rows){
  147. resultTable.Columns.Add(row["ColumnName"] as string, (Type)row["DataType"] as Type);
  148. }
  149. object[] values = new object[resultTable.Columns.Count];
  150. while (reader.Read()) {
  151. reader.GetValues(values);
  152. resultTable.Rows.Add(values);
  153. }
  154. }
  155. finally {
  156. if (reader != null) {
  157. reader.Dispose();
  158. reader = null;
  159. }
  160. }
  161. return resultTable;
  162. }
  163. private DataColumn[] FilterColumns(DataTable sourceTable, string[] hiddenColumnNames, DataColumnCollection destinationColumns) {
  164. DataColumn newDestinationColumn;
  165. int currentColumn;
  166. DataColumn[] filteredSourceColumns = null;
  167. int columnCount = 0;
  168. foreach (DataColumn sourceColumn in sourceTable.Columns){
  169. if (IncludeThisColumn(sourceColumn,hiddenColumnNames) == true) {
  170. columnCount++;
  171. }
  172. }
  173. if (columnCount == 0) {
  174. throw ADP.NoColumns();
  175. }
  176. currentColumn= 0;
  177. filteredSourceColumns = new DataColumn[columnCount];
  178. foreach(DataColumn sourceColumn in sourceTable.Columns){
  179. if (IncludeThisColumn(sourceColumn,hiddenColumnNames) == true) {
  180. newDestinationColumn = new DataColumn(sourceColumn.ColumnName,sourceColumn.DataType);
  181. destinationColumns.Add(newDestinationColumn);
  182. filteredSourceColumns[currentColumn] = sourceColumn;
  183. currentColumn++;
  184. }
  185. }
  186. return filteredSourceColumns;
  187. }
  188. internal DataRow FindMetaDataCollectionRow(string collectionName) {
  189. bool versionFailure;
  190. bool haveExactMatch;
  191. bool haveMultipleInexactMatches;
  192. string candidateCollectionName;
  193. DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
  194. if (metaDataCollectionsTable == null) {
  195. throw ADP.InvalidXml();
  196. }
  197. DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
  198. if ((null == collectionNameColumn) || (typeof(System.String) != collectionNameColumn.DataType)) {
  199. throw ADP.InvalidXmlMissingColumn(DbMetaDataCollectionNames.MetaDataCollections, DbMetaDataColumnNames.CollectionName);
  200. }
  201. DataRow requestedCollectionRow = null;
  202. String exactCollectionName = null;
  203. // find the requested collection
  204. versionFailure = false;
  205. haveExactMatch = false;
  206. haveMultipleInexactMatches = false;
  207. foreach (DataRow row in metaDataCollectionsTable.Rows){
  208. candidateCollectionName = row[collectionNameColumn,DataRowVersion.Current] as string;
  209. if (ADP.IsEmpty(candidateCollectionName)) {
  210. throw ADP.InvalidXmlInvalidValue(DbMetaDataCollectionNames.MetaDataCollections,DbMetaDataColumnNames.CollectionName);
  211. }
  212. if (ADP.CompareInsensitiveInvariant(candidateCollectionName, collectionName)){
  213. if (SupportedByCurrentVersion(row) == false) {
  214. versionFailure = true;
  215. }
  216. else{
  217. if (collectionName == candidateCollectionName) {
  218. if (haveExactMatch == true) {
  219. throw ADP.CollectionNameIsNotUnique(collectionName);
  220. }
  221. requestedCollectionRow = row;
  222. exactCollectionName = candidateCollectionName;
  223. haveExactMatch = true;
  224. }
  225. else {
  226. // have an inexact match - ok only if it is the only one
  227. if (exactCollectionName != null) {
  228. // can't fail here becasue we may still find an exact match
  229. haveMultipleInexactMatches = true;
  230. }
  231. requestedCollectionRow = row;
  232. exactCollectionName = candidateCollectionName;
  233. }
  234. }
  235. }
  236. }
  237. if (requestedCollectionRow == null){
  238. if (versionFailure == false) {
  239. throw ADP.UndefinedCollection(collectionName);
  240. }
  241. else {
  242. throw ADP.UnsupportedVersion(collectionName);
  243. }
  244. }
  245. if ((haveExactMatch == false) && (haveMultipleInexactMatches == true)) {
  246. throw ADP.AmbigousCollectionName(collectionName);
  247. }
  248. return requestedCollectionRow;
  249. }
  250. private void FixUpVersion(DataTable dataSourceInfoTable){
  251. Debug.Assert(dataSourceInfoTable.TableName == DbMetaDataCollectionNames.DataSourceInformation);
  252. DataColumn versionColumn = dataSourceInfoTable.Columns[_dataSourceProductVersion];
  253. DataColumn normalizedVersionColumn = dataSourceInfoTable.Columns[_dataSourceProductVersionNormalized];
  254. if ((versionColumn == null) || (normalizedVersionColumn == null)) {
  255. throw ADP.MissingDataSourceInformationColumn();
  256. }
  257. if (dataSourceInfoTable.Rows.Count != 1) {
  258. throw ADP.IncorrectNumberOfDataSourceInformationRows();
  259. }
  260. DataRow dataSourceInfoRow = dataSourceInfoTable.Rows[0];
  261. dataSourceInfoRow[versionColumn] = _serverVersionString;
  262. dataSourceInfoRow[normalizedVersionColumn] = _normalizedServerVersion;
  263. dataSourceInfoRow.AcceptChanges();
  264. }
  265. private string GetParameterName(string neededCollectionName, int neededRestrictionNumber) {
  266. DataTable restrictionsTable = null;
  267. DataColumnCollection restrictionColumns = null;
  268. DataColumn collectionName = null;
  269. DataColumn parameterName = null;
  270. DataColumn restrictionName = null;
  271. DataColumn restrictionNumber = null;;
  272. string result = null;
  273. restrictionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.Restrictions];
  274. if (restrictionsTable != null) {
  275. restrictionColumns = restrictionsTable.Columns;
  276. if (restrictionColumns != null) {
  277. collectionName = restrictionColumns[_collectionName];
  278. parameterName = restrictionColumns[_parameterName];
  279. restrictionName = restrictionColumns[_restrictionName];
  280. restrictionNumber = restrictionColumns[_restrictionNumber];
  281. }
  282. }
  283. if ((parameterName == null) ||(collectionName == null) || (restrictionName == null) || (restrictionNumber == null)) {
  284. throw ADP.MissingRestrictionColumn();
  285. }
  286. foreach (DataRow restriction in restrictionsTable.Rows) {
  287. if (((string)restriction[collectionName] == neededCollectionName) &&
  288. ((int)restriction[restrictionNumber] == neededRestrictionNumber) &&
  289. (SupportedByCurrentVersion(restriction))) {
  290. result = (string)restriction[parameterName];
  291. break;
  292. }
  293. }
  294. if (result == null) {
  295. throw ADP.MissingRestrictionRow();
  296. }
  297. return result;
  298. }
  299. virtual public DataTable GetSchema(DbConnection connection, string collectionName, string[] restrictions) {
  300. Debug.Assert (_metaDataCollectionsDataSet != null);
  301. //
  302. DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
  303. DataColumn populationMechanismColumn = metaDataCollectionsTable.Columns[_populationMechanism];
  304. DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
  305. DataRow requestedCollectionRow = null;
  306. DataTable requestedSchema = null;
  307. string[] hiddenColumns;
  308. string exactCollectionName = null;
  309. requestedCollectionRow = FindMetaDataCollectionRow(collectionName);
  310. exactCollectionName = requestedCollectionRow[collectionNameColumn,DataRowVersion.Current] as string;
  311. if (ADP.IsEmptyArray(restrictions) == false){
  312. for (int i = 0; i < restrictions.Length; i++) {
  313. if ((restrictions[i] != null) && (restrictions[i].Length > 4096)) {
  314. // use a non-specific error because no new beta 2 error messages are allowed
  315. //
  316. throw ADP.NotSupported();
  317. }
  318. }
  319. }
  320. string populationMechanism = requestedCollectionRow[populationMechanismColumn,DataRowVersion.Current] as string;
  321. switch (populationMechanism) {
  322. case _dataTable:
  323. if (exactCollectionName == DbMetaDataCollectionNames.MetaDataCollections) {
  324. hiddenColumns = new string[2];
  325. hiddenColumns[0] = _populationMechanism;
  326. hiddenColumns[1] = _populationString;
  327. }
  328. else {
  329. hiddenColumns = null;
  330. }
  331. // none of the datatable collections support restrictions
  332. if (ADP.IsEmptyArray(restrictions) == false){
  333. throw ADP.TooManyRestrictions(exactCollectionName);
  334. }
  335. requestedSchema = CloneAndFilterCollection(exactCollectionName,hiddenColumns);
  336. //
  337. // for the data source infomation table we need to fix up the version columns at run time
  338. // since the version is determined at run time
  339. if (exactCollectionName == DbMetaDataCollectionNames.DataSourceInformation) {
  340. FixUpVersion(requestedSchema);
  341. }
  342. break;
  343. case _sqlCommand:
  344. requestedSchema = ExecuteCommand(requestedCollectionRow,restrictions,connection);
  345. break;
  346. case _prepareCollection:
  347. requestedSchema = PrepareCollection(exactCollectionName,restrictions, connection);
  348. break;
  349. default:
  350. throw ADP.UndefinedPopulationMechanism(populationMechanism);
  351. }
  352. return requestedSchema;
  353. }
  354. private bool IncludeThisColumn(DataColumn sourceColumn, string[] hiddenColumnNames) {
  355. bool result = true;
  356. string sourceColumnName = sourceColumn.ColumnName;
  357. switch (sourceColumnName) {
  358. case _minimumVersion:
  359. case _maximumVersion:
  360. result = false;
  361. break;
  362. default:
  363. if (hiddenColumnNames == null) {
  364. break;
  365. }
  366. for (int i = 0 ; i < hiddenColumnNames.Length; i++) {
  367. if (hiddenColumnNames[i] == sourceColumnName){
  368. result = false;
  369. break;
  370. }
  371. }
  372. break;
  373. }
  374. return result;
  375. }
  376. private void LoadDataSetFromXml(Stream XmlStream){
  377. _metaDataCollectionsDataSet = new DataSet();
  378. _metaDataCollectionsDataSet.Locale = System.Globalization.CultureInfo.InvariantCulture;
  379. _metaDataCollectionsDataSet.ReadXml(XmlStream);
  380. }
  381. virtual protected DataTable PrepareCollection(String collectionName, String[] restrictions,DbConnection connection){
  382. throw ADP.NotSupported();
  383. }
  384. private bool SupportedByCurrentVersion(DataRow requestedCollectionRow){
  385. bool result = true;
  386. DataColumnCollection tableColumns = requestedCollectionRow.Table.Columns;
  387. DataColumn versionColumn;
  388. Object version;
  389. // check the minimum version first
  390. versionColumn = tableColumns[_minimumVersion];
  391. if (versionColumn != null) {
  392. version = requestedCollectionRow[versionColumn];
  393. if (version != null) {
  394. if (version != DBNull.Value) {
  395. if (0 > string.Compare( _normalizedServerVersion,(string)version, StringComparison.OrdinalIgnoreCase)){
  396. result = false;
  397. }
  398. }
  399. }
  400. }
  401. // if the minmum version was ok what about the maximum version
  402. if (result == true) {
  403. versionColumn = tableColumns[_maximumVersion];
  404. if (versionColumn != null) {
  405. version = requestedCollectionRow[versionColumn];
  406. if (version != null) {
  407. if (version != DBNull.Value) {
  408. if (0 < string.Compare( _normalizedServerVersion,(string)version, StringComparison.OrdinalIgnoreCase)){
  409. result = false;
  410. }
  411. }
  412. }
  413. }
  414. }
  415. return result;
  416. }
  417. }
  418. }