PageRenderTime 54ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/EFRepository/DependencyAnalyzer2008/SsisEnumerator.cs

#
C# | 1509 lines | 1150 code | 151 blank | 208 comment | 206 complexity | 0ff17fe1a5a83952218c52cc5eeb5274 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. ///
  2. /// Microsoft SQL Server 2008 Business Intelligence Metadata Reporting Samples
  3. /// Dependency Analyzer Sample
  4. ///
  5. /// Copyright (c) Microsoft Corporation. All rights reserved.
  6. ///
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Data.OleDb;
  10. using System.Diagnostics;
  11. using System.Text;
  12. using System.Xml;
  13. using Microsoft.SqlServer.Dts.Runtime;
  14. using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
  15. using DTR = Microsoft.SqlServer.Dts.Runtime.Wrapper;
  16. using System.Collections.Specialized;
  17. using TSQLParser;
  18. #if SQL2005
  19. using IDTSComponentMetaData = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSComponentMetaData90;
  20. using IDTSPipeline = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSPipeline90;
  21. using IDTSInput = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInput90;
  22. using IDTSInputColumn = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInputColumn90;
  23. using IDTSOutput = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSOutput90;
  24. using IDTSOutputColumn = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSOutputColumn90;
  25. using IDTSRuntimeConnection = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSRuntimeConnection90;
  26. using IDTSPath = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSPath90;
  27. using IDTSCustomProperty = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSCustomProperty90;
  28. #else
  29. using IDTSComponentMetaData = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSComponentMetaData100;
  30. using IDTSPipeline = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSPipeline100;
  31. using IDTSInput = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInput100;
  32. using IDTSInputColumn = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInputColumn100;
  33. using IDTSOutput = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSOutput100;
  34. using IDTSOutputColumn = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSOutputColumn100;
  35. using IDTSRuntimeConnection = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSRuntimeConnection100;
  36. using IDTSPath = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSPath100;
  37. using IDTSCustomProperty = Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSCustomProperty100;
  38. #endif
  39. namespace Microsoft.Samples.DependencyAnalyzer
  40. {
  41. /// <summary>
  42. /// Enumerates Integration Services objects and the relationships between them. The result of this enumeration is persisted
  43. /// in a repository.
  44. /// </summary>
  45. class SSISEnumerator
  46. {
  47. // 5 access modes used by ole db adapters
  48. enum AccessMode : int
  49. {
  50. AM_OPENROWSET = 0,
  51. AM_OPENROWSET_VARIABLE = 1,
  52. AM_SQLCOMMAND = 2,
  53. AM_SQLCOMMAND_VARIABLE = 3,
  54. AM_OPENROWSET_FASTLOAD_VARIABLE = 4
  55. }
  56. enum SqlStatementSourceType : int
  57. {
  58. DirectInput = 1,
  59. FileConnection = 2,
  60. Variable = 3
  61. }
  62. private Application app;
  63. private Repository repository;
  64. private const string dtsxPattern = "*.dtsx";
  65. /// <summary>
  66. /// Different component Class IDs that we understand about
  67. /// </summary>
  68. private class ClassIDs
  69. {
  70. #if SQL2005
  71. internal const string OleDbSource = "{2C0A8BE5-1EDC-4353-A0EF-B778599C65A0}";
  72. internal const string ExcelSource = "{B551FCA8-23BD-4719-896F-D8F352A5283C}";
  73. internal const string FlatFileSource = "{90C7770B-DE7C-435E-880E-E718C92C0573}";
  74. internal const string RawFileSource = "{E2568105-9550-4F71-A638-B7FE42E66930}";
  75. internal const string XmlSource = "Microsoft.SqlServer.Dts.Pipeline.XmlSourceAdapter, Microsoft.SqlServer.XmlSrc, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91";
  76. internal const string OleDbDestination = "{E2568105-9550-4F71-A638-B7FE42E66922}";
  77. internal const string SqlDestination = "{5244B484-7C76-4026-9A01-00928EA81550}";
  78. internal const string FlatFileDest = "{A1DF9F6D-8EE4-4EF0-BB2E-D526130D7A7B}";
  79. internal const string RawFileDest = "{E2568105-9550-4F71-A638-B7FE42E66933}";
  80. internal const string Lookup = "{0FB4AABB-C027-4440-809A-1198049BF117}";
  81. internal const string FuzzyLookup = "{9F4EB4D4-AD71-496D-B70B-31ECE1139884}";
  82. internal const string ManagedComponentWrapper = "{bf01d463-7089-41ee-8f05-0a6dc17ce633}";
  83. internal const string DerivedColumn = "{9CF90BF0-5BCC-4C63-B91D-1F322DC12C26}";
  84. internal const string MultipleHash = "Martin.SQLServer.Dts.MultipleHash, MultipleHash2005, Version=1.0.0.0, Culture=neutral, PublicKeyToken=51c551904274ab44";
  85. internal const string KimballSCD = "MouldingAndMillwork.SSIS.KimballMethodSCD, KimballMethodSCD90, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8b0551303405e96c";
  86. internal const string OLEDBCommand = "{C60ACAD1-9BE8-46B3-87DA-70E59EADEA46}";
  87. #else
  88. internal const string OleDbSource = "{BCEFE59B-6819-47F7-A125-63753B33ABB7}";
  89. internal const string ExcelSource = "{A4B1E1C8-17F3-46C8-AAD0-34F0C6FE42DE}";
  90. internal const string FlatFileSource = "{5ACD952A-F16A-41D8-A681-713640837664}";
  91. internal const string RawFileSource = "{51DC0B24-7421-45C3-B4AB-9481A683D91D}";
  92. internal const string XmlSource = "Microsoft.SqlServer.Dts.Pipeline.XmlSourceAdapter, Microsoft.SqlServer.XmlSrc, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91";
  93. internal const string OleDbDestination = "{5A0B62E8-D91D-49F5-94A5-7BE58DE508F0}";
  94. internal const string SqlDestination = "{E4B61516-847B-4BDF-9CC6-1968A2D43E73}";
  95. internal const string FlatFileDest = "{D658C424-8CF0-441C-B3C4-955E183B7FBA}";
  96. internal const string RawFileDest = "{485E7329-8754-42B4-AA5B-29C5DA09CAD5}";
  97. internal const string ExcelDestination = "{C9269E28-EBDE-4DED-91EB-0BF42842F9F4}";
  98. internal const string Lookup = "{27648839-180F-45E6-838D-AFF53DF682D2}";
  99. internal const string FuzzyLookup = "{5056651F-F227-4978-94DF-53CDF9E8CCB6}";
  100. internal const string ManagedComponentWrapper = "{2E42D45B-F83C-400F-8D77-61DDE6A7DF29}";
  101. internal const string DerivedColumn = "{2932025B-AB99-40F6-B5B8-783A73F80E24}";
  102. internal const string MultipleHash = "Martin.SQLServer.Dts.MultipleHash, MultipleHash2008, Version=1.0.0.0, Culture=neutral, PublicKeyToken=51c551904274ab44";
  103. internal const string KimballSCD = "MouldingAndMillwork.SSIS.KimballMethodSCD, KimballMethodSCD100, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8b0551303405e96c";
  104. internal const string OLEDBCommand = "{8E61C8F6-C91D-43B6-97EB-3423C06571CC}";
  105. #endif
  106. }
  107. private const string objectTypePackage = "SSIS Package";
  108. private const string attributePackageLocation = "PackageLocation";
  109. private const string attributePackageID = "PackageGUID";
  110. private OleDbConnectionStringBuilder connectionStringBuilder = new OleDbConnectionStringBuilder();
  111. /// <summary>
  112. /// information about registered pipeline components
  113. /// </summary>
  114. private PipelineComponentInfos pipelineComponentInfos = null;
  115. /// <summary>
  116. /// information about registered connections
  117. /// </summary>
  118. private ConnectionInfos connectionInfos = null;
  119. /// <summary>
  120. /// information about registered tasks
  121. /// </summary>
  122. private TaskInfos taskInfos = null;
  123. private Dictionary<string, string> connectionTypeToIDMap = null;
  124. public SSISEnumerator()
  125. {
  126. }
  127. public bool Initialize(Repository repository)
  128. {
  129. bool success;
  130. try
  131. {
  132. this.repository = repository;
  133. // lets handle the infos enumeration right here.
  134. app = new Application();
  135. EnumerateInfos(app);
  136. success = true;
  137. }
  138. catch (System.Exception ex)
  139. {
  140. Console.WriteLine(string.Format("Could not initialize the SSIS Packages Enumerator: {0}", ex.Message));
  141. success = false;
  142. }
  143. return success;
  144. }
  145. /// <summary>
  146. /// enumerates all packages stored off in SQL Server database
  147. /// </summary>
  148. /// <param name="sqlConnectionString"></param>
  149. public void EnumerateSqlPackages(string server, string user, string pwd, string[] rootFolders)
  150. {
  151. try
  152. {
  153. Queue<string> folders = new Queue<string>();
  154. foreach (string folderName in rootFolders)
  155. {
  156. folders.Enqueue(folderName);
  157. }
  158. if (folders.Count == 0)
  159. {
  160. folders.Enqueue("\\"); // the root folder
  161. }
  162. do
  163. {
  164. string folder = folders.Dequeue();
  165. PackageInfos packageInfos = app.GetPackageInfos(folder, server, user, pwd);
  166. foreach (PackageInfo packageInfo in packageInfos)
  167. {
  168. string location = packageInfo.Folder + "\\" + packageInfo.Name;
  169. if (packageInfo.Flags == DTSPackageInfoFlags.Folder)
  170. {
  171. folders.Enqueue(location);
  172. }
  173. else
  174. {
  175. Debug.Assert(packageInfo.Flags == DTSPackageInfoFlags.Package);
  176. try
  177. {
  178. Console.Write(string.Format("Loading SQL package '{0}'... ", location));
  179. using (Package package = app.LoadFromSqlServer(location, server, user, pwd, null))
  180. {
  181. EnumeratePackage(package, location);
  182. }
  183. Console.WriteLine("Completed Successfully.");
  184. }
  185. catch (System.Exception ex2)
  186. {
  187. Console.WriteLine(string.Format("Error occurred: '{0}'", ex2.Message));
  188. }
  189. }
  190. }
  191. } while (folders.Count > 0);
  192. }
  193. catch (System.Exception ex)
  194. {
  195. Console.WriteLine(string.Format("Error enumerating packages on SQL Server '{0}': {1}", server, ex.Message));
  196. }
  197. }
  198. /// <summary>
  199. /// Enumerates all packages that're in a directory and all sub directories if user asked us to.
  200. /// </summary>
  201. /// <param name="rootFolders"></param>
  202. /// <param name="recurseSubFolders"></param>
  203. public void EnumerateFileSystemPackages(string[] rootFolders, bool recurseSubFolders)
  204. {
  205. foreach (string rootFolder in rootFolders)
  206. {
  207. if (System.IO.Directory.Exists(rootFolder) == false)
  208. {
  209. throw new Exception(string.Format("Root package folder '{0}' doesn't exist.", rootFolder));
  210. }
  211. EnumeratePackages(rootFolder, dtsxPattern, recurseSubFolders);
  212. }
  213. }
  214. private void EnumeratePackages(string rootFolder, string pattern, bool recurseSubFolders)
  215. {
  216. Console.Write("Enumerating packages...");
  217. string[] filesToInspect = System.IO.Directory.GetFiles(rootFolder, pattern, (recurseSubFolders) ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly);
  218. Console.WriteLine("done.");
  219. foreach (string packageFileName in filesToInspect)
  220. {
  221. try
  222. {
  223. Console.Write(string.Format("Loading file package '{0}'... ", packageFileName));
  224. // load the package
  225. using (Package package = app.LoadPackage(packageFileName, null))
  226. {
  227. EnumeratePackage(package, packageFileName);
  228. }
  229. Console.WriteLine("Completed Successfully.");
  230. }
  231. catch (System.Exception ex)
  232. {
  233. Console.WriteLine(string.Format("Error occurred: '{0}'", ex.Message));
  234. }
  235. }
  236. }
  237. private void EnumerateInfos(Application app)
  238. {
  239. Console.Write("Enumerating registered SSIS Data Flow components...");
  240. pipelineComponentInfos = app.PipelineComponentInfos;
  241. foreach (PipelineComponentInfo pipelineComponentInfo in pipelineComponentInfos)
  242. {
  243. repository.AddObjectType(Repository.Domains.SSIS, pipelineComponentInfo.ID, pipelineComponentInfo.Name, pipelineComponentInfo.Description);
  244. }
  245. Console.WriteLine("Done");
  246. Console.Write("Enumerating registered SSIS Connection Managers...");
  247. connectionInfos = app.ConnectionInfos;
  248. connectionTypeToIDMap = new Dictionary<string, string>(connectionInfos.Count);
  249. foreach (ConnectionInfo connectionInfo in connectionInfos)
  250. {
  251. connectionTypeToIDMap.Add(connectionInfo.ConnectionType, connectionInfo.ID);
  252. repository.AddObjectType(Repository.Domains.SSIS, connectionInfo.ID, connectionInfo.Name, connectionInfo.Description);
  253. }
  254. Console.WriteLine("Done");
  255. Console.Write("Enumerating registered SSIS Tasks...");
  256. taskInfos = app.TaskInfos;
  257. foreach (TaskInfo taskInfo in taskInfos)
  258. {
  259. repository.AddObjectType(Repository.Domains.SSIS, taskInfo.ID, taskInfo.Name, taskInfo.Description);
  260. }
  261. Console.WriteLine("Done");
  262. }
  263. /// <summary>
  264. /// recurses through all dtsContainers looking for any pipelines...
  265. /// Now handles ForEachLoop, ForLoop and Sequence Containers
  266. /// Disabled Tasks are not tested...
  267. /// </summary>
  268. /// <param name="package">The SSIS Package that is being investigated</param>
  269. /// <param name="location">The text string of the location of this package</param>
  270. /// <param name="packageRepositoryID">The internal ID for this package</param>
  271. /// <param name="dtsContainer">The Container that is to be assessed</param>
  272. private void EnumerateTask(Package package, string location, int packageRepositoryID, DtsContainer dtsContainer)
  273. {
  274. if (!dtsContainer.Disable)
  275. {
  276. if (dtsContainer is TaskHost)
  277. {
  278. TaskHost taskHost = dtsContainer as TaskHost;
  279. IDTSPipeline pipeline = taskHost.InnerObject as IDTSPipeline;
  280. if (pipeline != null)
  281. {
  282. // this is a data flow task
  283. packageRepositoryID = InspectDataFlow(pipeline, packageRepositoryID, package, taskHost, location);
  284. }
  285. else if (dtsContainer.CreationName.Contains("Microsoft.SqlServer.Dts.Tasks.ExecuteSQLTask.ExecuteSQLTask"))
  286. {
  287. EnumerateSqlTask(package, location, packageRepositoryID, taskHost);
  288. }
  289. }
  290. else if (dtsContainer is ForEachLoop)
  291. {
  292. ForEachLoop felContainer = dtsContainer as ForEachLoop;
  293. foreach (DtsContainer innerDtsContainer in felContainer.Executables)
  294. {
  295. EnumerateTask(package, location, packageRepositoryID, innerDtsContainer);
  296. }
  297. }
  298. else if (dtsContainer is ForLoop)
  299. {
  300. ForLoop flContainer = dtsContainer as ForLoop;
  301. foreach (DtsContainer innerDtsContainer in flContainer.Executables)
  302. {
  303. EnumerateTask(package, location, packageRepositoryID, innerDtsContainer);
  304. }
  305. }
  306. else if (dtsContainer is Sequence)
  307. {
  308. Sequence sContainer = dtsContainer as Sequence;
  309. foreach (DtsContainer innerDtsContainer in sContainer.Executables)
  310. {
  311. EnumerateTask(package, location, packageRepositoryID, innerDtsContainer);
  312. }
  313. }
  314. }
  315. }
  316. /// <summary>
  317. /// Handles parsing an SQLTask object, and finding what is used within it.
  318. /// </summary>
  319. /// <param name="package">The SSIS Package that is being investigated</param>
  320. /// <param name="location">The text string of the location of this package</param>
  321. /// <param name="packageRepositoryID">The internal ID for this package</param>
  322. /// <param name="taskHost">The SQL Task that is to be investigated.</param>
  323. private void EnumerateSqlTask(Package package, string location, int packageRepositoryID, TaskHost taskHost)
  324. {
  325. if (packageRepositoryID == -1)
  326. {
  327. // add this package to the repository
  328. packageRepositoryID = AddObject(package.Name, package.Description, objectTypePackage, repository.RootRepositoryObjectID);
  329. repository.AddAttribute(packageRepositoryID, attributePackageLocation, location);
  330. repository.AddAttribute(packageRepositoryID, attributePackageID, package.ID);
  331. }
  332. // Add this task to the repository
  333. int sqlTaskRepositoryObjectID = AddObject(taskHost.Name, taskHost.Description, taskHost.CreationName, packageRepositoryID);
  334. if (taskHost.Properties.Contains("SqlStatementSource") & taskHost.Properties.Contains("SqlStatementSourceType") & taskHost.Properties.Contains("Connection"))
  335. {
  336. string queryDefinition = string.Empty;
  337. SqlStatementSourceType stmtSource = (SqlStatementSourceType)taskHost.Properties["SqlStatementSourceType"].GetValue(taskHost);
  338. switch (stmtSource)
  339. {
  340. case SqlStatementSourceType.DirectInput:
  341. queryDefinition = taskHost.Properties["SqlStatementSource"].GetValue(taskHost).ToString();
  342. repository.AddAttribute(sqlTaskRepositoryObjectID, Repository.Attributes.QueryDefinition, queryDefinition);
  343. break;
  344. case SqlStatementSourceType.FileConnection:
  345. break;
  346. case SqlStatementSourceType.Variable:
  347. queryDefinition = GetVariable(package, taskHost.Properties["SqlStatementSource"].GetValue(taskHost).ToString());
  348. repository.AddAttribute(sqlTaskRepositoryObjectID, Repository.Attributes.QueryDefinition, queryDefinition);
  349. break;
  350. default:
  351. throw new Exception(string.Format("Invalid Sql Statement Source Type {0}.", stmtSource));
  352. }
  353. ConnectionManager connectionManager = package.Connections[taskHost.Properties["Connection"].GetValue(taskHost).ToString()];
  354. string connectionManagerType = connectionManager.CreationName;
  355. int connectionID = repository.GetConnection(connectionManager.ConnectionString);
  356. string serverName = null;
  357. if (connectionID == -1)
  358. {
  359. connectionID = CreateConnection(connectionManager, out serverName);
  360. }
  361. if (!string.IsNullOrEmpty(queryDefinition))
  362. {
  363. try
  364. {
  365. SqlStatement toBeParsed = new SqlStatement();
  366. //toBeParsed.sqlString = statement;
  367. toBeParsed.quotedIdentifiers = true;
  368. if (toBeParsed.ParseString(queryDefinition))
  369. {
  370. foreach (string tableName in toBeParsed.getTableNames(true))
  371. {
  372. int tableID = repository.GetTable(connectionID, tableName);
  373. repository.AddMapping(tableID, sqlTaskRepositoryObjectID);
  374. }
  375. foreach (string procedureName in toBeParsed.getProcedureNames(true))
  376. {
  377. int procID = repository.GetProcedure(connectionID, procedureName);
  378. repository.AddMapping(procID, sqlTaskRepositoryObjectID);
  379. }
  380. foreach (string funcName in toBeParsed.getFunctionNames(true))
  381. {
  382. int funcID = repository.GetFunction(connectionID, funcName);
  383. repository.AddMapping(funcID, sqlTaskRepositoryObjectID);
  384. }
  385. }
  386. else
  387. {
  388. string errorMessage = "The following messages where generated whilst parsing the sql statement\r\n" + queryDefinition + "\r\n";
  389. foreach (string error in toBeParsed.parseErrors)
  390. {
  391. errorMessage += error + "\r\n";
  392. }
  393. Console.WriteLine(errorMessage);
  394. }
  395. }
  396. catch (System.Exception err)
  397. {
  398. Console.WriteLine("The exception \r\n{0}\r\nwas raised against query \r\n{1}\r\nPlease report to http://sqlmetadata.codeplex.com/WorkItem/List.aspx\r\n", err.Message, queryDefinition);
  399. }
  400. }
  401. }
  402. //ExecuteSQLTask sqlTask = taskHost.InnerObject as ExecuteSQLTask;
  403. //string queryDefinition = sqlTask.SqlStatementSource;
  404. //// add the query definition attribute if we have one
  405. //if (queryDefinition != null)
  406. //{
  407. // repository.AddAttribute(sqlTaskRepositoryObjectID, Repository.Attributes.QueryDefinition, queryDefinition);
  408. //}
  409. }
  410. /// <summary>
  411. /// handles loading information about a package to the repository
  412. /// </summary>
  413. /// <param name="package"></param>
  414. private void EnumeratePackage(Package package, string location)
  415. {
  416. // add this package to the repository
  417. int packageRepositoryID = AddObject(package.Name, package.Description, objectTypePackage, repository.RootRepositoryObjectID);
  418. repository.AddAttribute(packageRepositoryID, attributePackageLocation, location);
  419. repository.AddAttribute(packageRepositoryID, attributePackageID, package.ID);
  420. // loop through all data flow tasks
  421. foreach (DtsContainer dtsContainer in package.Executables)
  422. {
  423. EnumerateTask(package, location, packageRepositoryID, dtsContainer);
  424. }
  425. //foreach (Executable executable in package.Executables)
  426. //{
  427. // if (executable is TaskHost)
  428. // {
  429. // if ((executable as TaskHost).InnerObject is ExecuteSQLTask)
  430. // {
  431. // EnumerateSqlTask(package, location, packageRepositoryID, (executable as TaskHost));
  432. // }
  433. // }
  434. //}
  435. }
  436. /// <summary>
  437. /// adds interesting components to the repository. If at least one interesting object exists in the data flow task
  438. /// the data flow task is also added to the repository. If at least one such interesting data flow task exists in the
  439. /// package and the package hasn't been created in the repository as yet, it will be created as well. What's returned
  440. /// is the package repository ID.
  441. /// </summary>
  442. /// <param name="packageRepositoryID"></param>
  443. /// <param name="package"></param>
  444. /// <param name="taskHost"></param>
  445. /// <returns></returns>
  446. private int InspectDataFlow(IDTSPipeline pipeline, int packageRepositoryID, Package package, TaskHost taskHost, string packageLocation)
  447. {
  448. int dataFlowRepositoryObjectID = -1;
  449. Dictionary<int, int> componentIDToSourceRepositoryObjectMap = new Dictionary<int, int>();
  450. Dictionary<int, int> componentIDToRepositoryObjectMap = new Dictionary<int, int>();
  451. // go through all components looking for sources or destinations
  452. foreach (IDTSComponentMetaData component in pipeline.ComponentMetaDataCollection)
  453. {
  454. EnumerateDataFlowComponent(ref packageRepositoryID, package, taskHost, packageLocation, ref dataFlowRepositoryObjectID, ref componentIDToSourceRepositoryObjectMap, ref componentIDToRepositoryObjectMap, component);
  455. }
  456. // after all the objects have been added, traverse their mappings
  457. if (componentIDToSourceRepositoryObjectMap.Count > 0)
  458. {
  459. EnumerateMappings(pipeline, dataFlowRepositoryObjectID, componentIDToSourceRepositoryObjectMap, componentIDToRepositoryObjectMap);
  460. }
  461. return packageRepositoryID;
  462. }
  463. private void EnumerateDataFlowComponent(ref int packageRepositoryID, Package package, TaskHost taskHost, string packageLocation, ref int dataFlowRepositoryObjectID, ref Dictionary<int, int> componentIDToSourceRepositoryObjectMap, ref Dictionary<int, int> componentIDToRepositoryObjectMap, IDTSComponentMetaData component)
  464. {
  465. string objectType;
  466. DTSPipelineComponentType dataFlowComponentType;
  467. string domain, tableOrViewName, queryDefinition;
  468. bool tableOrViewSource;
  469. string kimballPropertyValue;
  470. XmlReaderSettings settings = new XmlReaderSettings();
  471. settings.IgnoreComments = true;
  472. settings.IgnoreProcessingInstructions = true;
  473. settings.IgnoreWhitespace = true;
  474. XmlReader objXMLText;
  475. // if we have a source, note it as the starting point and add it to the repository
  476. if (IsComponentInteresting(package, component, out objectType, out dataFlowComponentType, out domain, out tableOrViewName, out tableOrViewSource, out queryDefinition))
  477. {
  478. // if the data flow object itself hasn't been created as yet, do so now
  479. if (dataFlowRepositoryObjectID == -1)
  480. {
  481. // if the package itself doesn't exist either, create it now
  482. if (packageRepositoryID == -1)
  483. {
  484. // add this package to the repository
  485. packageRepositoryID = AddObject(package.Name, package.Description, objectTypePackage, repository.RootRepositoryObjectID);
  486. repository.AddAttribute(packageRepositoryID, attributePackageLocation, packageLocation);
  487. repository.AddAttribute(packageRepositoryID, attributePackageID, package.ID);
  488. }
  489. dataFlowRepositoryObjectID = AddObject(taskHost.Name, taskHost.Description, taskHost.CreationName, packageRepositoryID);
  490. }
  491. // add the component to the repository
  492. int componentRepositoryID = AddObject(component.Name, component.Description, objectType, dataFlowRepositoryObjectID);
  493. Debug.Assert(componentRepositoryID > 0);
  494. if (dataFlowComponentType == DTSPipelineComponentType.SourceAdapter)
  495. {
  496. componentIDToSourceRepositoryObjectMap.Add(component.ID, componentRepositoryID);
  497. }
  498. componentIDToRepositoryObjectMap.Add(component.ID, componentRepositoryID);
  499. // add the query definition attribute if we have one
  500. if (queryDefinition != null)
  501. {
  502. repository.AddAttribute(componentRepositoryID, Repository.Attributes.QueryDefinition, queryDefinition);
  503. }
  504. if (component.ComponentClassID == ClassIDs.OLEDBCommand)
  505. {
  506. GetStringComponentPropertyValue(component, "SqlCommand");
  507. }
  508. else if (component.ComponentClassID == ClassIDs.DerivedColumn)
  509. {
  510. foreach (IDTSInput localInput in component.InputCollection)
  511. {
  512. if (localInput.IsAttached)
  513. {
  514. foreach (IDTSInputColumn localIColumn in localInput.InputColumnCollection)
  515. {
  516. if (localIColumn.CustomPropertyCollection.Count == 2)
  517. {
  518. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale) + " Expression " + localIColumn.CustomPropertyCollection["FriendlyExpression"].Value.ToString());
  519. }
  520. else
  521. {
  522. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale) + " Expression (See Ouput Column)");
  523. }
  524. //repository.AddObject(localIColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  525. }
  526. }
  527. }
  528. foreach (IDTSOutput localOutput in component.OutputCollection)
  529. {
  530. if (localOutput.IsAttached)
  531. {
  532. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  533. {
  534. if (localOColumn.CustomPropertyCollection.Count == 2)
  535. {
  536. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale) + " Expression " + localOColumn.CustomPropertyCollection["FriendlyExpression"].Value.ToString());
  537. }
  538. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  539. //ToDo: Add connection from Input to Output Column.
  540. }
  541. }
  542. }
  543. }
  544. else if (component.ComponentClassID == ClassIDs.Lookup)
  545. {
  546. foreach (IDTSInput localInput in component.InputCollection)
  547. {
  548. if (localInput.IsAttached)
  549. {
  550. foreach (IDTSInputColumn localIColumn in localInput.InputColumnCollection)
  551. {
  552. if (localIColumn.CustomPropertyCollection.Count == 2)
  553. {
  554. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale) + " Reference Column [" + localIColumn.CustomPropertyCollection["JoinToReferenceColumn"].Value.ToString() + "]");
  555. }
  556. else
  557. {
  558. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale) + " Expression (See Ouput Column)");
  559. }
  560. //repository.AddObject(localIColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  561. }
  562. }
  563. }
  564. foreach (IDTSOutput localOutput in component.OutputCollection)
  565. {
  566. if (localOutput.IsAttached)
  567. {
  568. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  569. {
  570. if (localOColumn.CustomPropertyCollection.Count == 1)
  571. {
  572. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale) + " Return Column [" + localOColumn.CustomPropertyCollection["CopyFromReferenceColumn"].Value.ToString() + "]");
  573. }
  574. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  575. }
  576. }
  577. }
  578. }
  579. else if (component.ComponentClassID == ClassIDs.FuzzyLookup)
  580. {
  581. foreach (IDTSInput localInput in component.InputCollection)
  582. {
  583. if (localInput.IsAttached)
  584. {
  585. foreach (IDTSInputColumn localIColumn in localInput.InputColumnCollection)
  586. {
  587. if (localIColumn.CustomPropertyCollection.Count == 5)
  588. {
  589. if (localIColumn.CustomPropertyCollection["JoinToReferenceColumn"].Value != null)
  590. {
  591. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale) + " Reference Column [" + localIColumn.CustomPropertyCollection["JoinToReferenceColumn"].Value.ToString() + "]");
  592. }
  593. else
  594. {
  595. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale));
  596. }
  597. }
  598. else
  599. {
  600. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale) + " Expression (See Ouput Column)");
  601. }
  602. //repository.AddObject(localIColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  603. }
  604. }
  605. }
  606. foreach (IDTSOutput localOutput in component.OutputCollection)
  607. {
  608. if (localOutput.IsAttached)
  609. {
  610. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  611. {
  612. if (localOColumn.CustomPropertyCollection.Count == 3)
  613. {
  614. if (localOColumn.CustomPropertyCollection["CopyFromReferenceColumn"].Value != null)
  615. {
  616. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale) + " Return Column [" + localOColumn.CustomPropertyCollection["CopyFromReferenceColumn"].Value.ToString() + "]");
  617. }
  618. else
  619. {
  620. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale));
  621. }
  622. }
  623. else
  624. {
  625. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale));
  626. }
  627. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  628. }
  629. }
  630. }
  631. }
  632. else if (component.ComponentClassID == ClassIDs.ManagedComponentWrapper)
  633. {
  634. if (component.CustomPropertyCollection["UserComponentTypeName"].Value.ToString() == ClassIDs.MultipleHash)
  635. {
  636. foreach (IDTSInput localInput in component.InputCollection)
  637. {
  638. if (localInput.IsAttached)
  639. {
  640. foreach (IDTSInputColumn localIColumn in localInput.InputColumnCollection)
  641. {
  642. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale));
  643. //repository.AddObject(localIColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  644. }
  645. }
  646. }
  647. foreach (IDTSOutput localOutput in component.OutputCollection)
  648. {
  649. if (localOutput.IsAttached)
  650. {
  651. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  652. {
  653. if (localOColumn.CustomPropertyCollection.Count == 2)
  654. {
  655. string localResults = "";
  656. if (localOColumn.CustomPropertyCollection["InputColumnLineageIDs"].Value.ToString().Length > 0)
  657. {
  658. foreach (string localIDs in localOColumn.CustomPropertyCollection["InputColumnLineageIDs"].Value.ToString().Split(','))
  659. {
  660. string withoutHash = localIDs;
  661. if (localIDs[0] == '#')
  662. {
  663. withoutHash = localIDs.Substring(1);
  664. }
  665. if (localResults.Length == 0)
  666. {
  667. localResults = component.InputCollection[0].InputColumnCollection.GetInputColumnByLineageID(System.Convert.ToInt32(withoutHash)).Name;
  668. }
  669. else
  670. {
  671. localResults += ", " + component.InputCollection[0].InputColumnCollection.GetInputColumnByLineageID(System.Convert.ToInt32(withoutHash)).Name;
  672. }
  673. }
  674. }
  675. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale) + " Generated From " + localResults);
  676. }
  677. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  678. }
  679. }
  680. }
  681. }
  682. else if (component.CustomPropertyCollection["UserComponentTypeName"].Value.ToString() == ClassIDs.KimballSCD)
  683. {
  684. foreach (IDTSInput localInput in component.InputCollection)
  685. {
  686. if (localInput.IsAttached)
  687. {
  688. foreach (IDTSInputColumn localIColumn in localInput.InputColumnCollection)
  689. {
  690. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale));
  691. //repository.AddObject(localIColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  692. }
  693. }
  694. }
  695. foreach (IDTSOutput localOutput in component.OutputCollection)
  696. {
  697. if (localOutput.IsAttached)
  698. {
  699. kimballPropertyValue = string.Empty;
  700. switch (localOutput.Name)
  701. {
  702. case "New":
  703. kimballPropertyValue = component.CustomPropertyCollection["New Output Columns"].Value.ToString();
  704. break;
  705. case "Unchanged":
  706. kimballPropertyValue = component.CustomPropertyCollection["Unchanged Output Columns"].Value.ToString();
  707. break;
  708. case "Deleted":
  709. kimballPropertyValue = component.CustomPropertyCollection["Deleted Output Columns"].Value.ToString();
  710. break;
  711. case "Updated SCD1":
  712. kimballPropertyValue = component.CustomPropertyCollection["Update SCD1 Columns"].Value.ToString();
  713. break;
  714. case "Auditing":
  715. kimballPropertyValue = component.CustomPropertyCollection["Audit Columns"].Value.ToString();
  716. break;
  717. case "Expired SCD2":
  718. kimballPropertyValue = component.CustomPropertyCollection["Expired SCD2 Columns"].Value.ToString();
  719. break;
  720. case "Invalid Input":
  721. break;
  722. case "New SCD2":
  723. kimballPropertyValue = component.CustomPropertyCollection["New SCD2 Columns"].Value.ToString();
  724. break;
  725. default:
  726. break;
  727. }
  728. string localResults = "Unknown.";
  729. if (kimballPropertyValue != string.Empty)
  730. {
  731. objXMLText = XmlReader.Create(new System.IO.StringReader(kimballPropertyValue), settings);
  732. objXMLText.Read();
  733. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  734. {
  735. string strLineageID;
  736. string strMappedToExistingDimensionLineageID;
  737. objXMLText.ReadToFollowing("OutputColumnDefinition");
  738. do
  739. {
  740. localResults = "Unknown.";
  741. strLineageID = objXMLText.GetAttribute("LineageID");
  742. strMappedToExistingDimensionLineageID = objXMLText.GetAttribute("MappedToExistingDimensionLineageID");
  743. if (localOColumn.LineageID == System.Convert.ToInt32(strLineageID))
  744. {
  745. foreach (IDTSInput localInput in component.InputCollection)
  746. {
  747. IDTSInputColumn temp;
  748. try
  749. {
  750. temp = localInput.InputColumnCollection.GetInputColumnByLineageID(System.Convert.ToInt32(strMappedToExistingDimensionLineageID));
  751. localResults = "[" + localInput.Name + "] Column [" + temp.Name + "].";
  752. break;
  753. }
  754. catch (Exception)
  755. {
  756. }
  757. }
  758. break;
  759. }
  760. }
  761. while (objXMLText.ReadToFollowing("OutputColumnDefinition"));
  762. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale) + " Generated From " + localResults);
  763. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  764. }
  765. }
  766. else
  767. {
  768. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  769. {
  770. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale) + " Generated From " + localResults);
  771. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  772. }
  773. }
  774. }
  775. }
  776. }
  777. else
  778. {
  779. AddColumnNameAttributes(component, componentRepositoryID);
  780. }
  781. }
  782. else
  783. {
  784. // Add generic column attribute information here.
  785. AddColumnNameAttributes(component, componentRepositoryID);
  786. }
  787. EnumerateComponentConnections(package, component, objectType, dataFlowComponentType, tableOrViewName, tableOrViewSource, queryDefinition, componentRepositoryID);
  788. }
  789. }
  790. private string FormatColumnDescription(string lName, Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType lType, int lLength, int lPrecision, int lScale)
  791. {
  792. string lTemp = "Column [" + lName + "] of Type [" + lType.ToString() + "]";
  793. switch (lType)
  794. {
  795. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_BYREF_DECIMAL:
  796. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_BYREF_NUMERIC:
  797. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_DECIMAL:
  798. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_NUMERIC:
  799. lTemp += " Precision " + lPrecision.ToString() + " Scale " + lScale.ToString();
  800. break;
  801. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_BYTES:
  802. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_IMAGE:
  803. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_NTEXT:
  804. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_STR:
  805. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_TEXT:
  806. case Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_WSTR:
  807. lTemp += " Length " + lLength;
  808. break;
  809. default:
  810. break;
  811. }
  812. return lTemp;
  813. }
  814. private void AddColumnNameAttributes(IDTSComponentMetaData component, int componentRepositoryID)
  815. {
  816. foreach (IDTSInput localInput in component.InputCollection)
  817. {
  818. if (localInput.IsAttached)
  819. {
  820. foreach (IDTSInputColumn localIColumn in localInput.InputColumnCollection)
  821. {
  822. repository.AddAttribute(componentRepositoryID, localInput.Name + " [" + localIColumn.Name + "] [ID: " + localIColumn.ID.ToString() + "]", "From [" + localIColumn.UpstreamComponentName + "] " + FormatColumnDescription(localIColumn.Name, localIColumn.DataType, localIColumn.Length, localIColumn.Precision, localIColumn.Scale));
  823. //repository.AddObject(localIColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  824. }
  825. }
  826. }
  827. foreach (IDTSOutput localOutput in component.OutputCollection)
  828. {
  829. if (localOutput.IsAttached)
  830. {
  831. foreach (IDTSOutputColumn localOColumn in localOutput.OutputColumnCollection)
  832. {
  833. repository.AddAttribute(componentRepositoryID, localOutput.Name + " [" + localOColumn.Name + "] [ID: " + localOColumn.ID.ToString() + "]", FormatColumnDescription(localOColumn.Name, localOColumn.DataType, localOColumn.Length, localOColumn.Precision, localOColumn.Scale));
  834. //repository.AddObject(localOColumn.Name, "", ColumnEnumerator.ObjectTypes.Column, componentRepositoryID);
  835. }
  836. }
  837. }
  838. }
  839. private void AddTableMappings(DTSPipelineComponentType dataFlowComponentType, bool tableOrViewSource, int componentRepositoryID, int tableRepositoryID)
  840. {
  841. if (tableRepositoryID != -1)
  842. {
  843. // add the mapping between the source table and the source component
  844. if ((dataFlowComponentType == DTSPipelineComponentType.SourceAdapter)
  845. || ((dataFlowComponentType == DTSPipelineComponentType.Transform) && (tableOrViewSource = true)))
  846. {
  847. repository.AddMapping(tableRepositoryID, componentRepositoryID);
  848. }
  849. else if ((dataFlowComponentType == DTSPipelineComponentType.DestinationAdapter)
  850. || ((dataFlowComponentType == DTSPipelineComponentType.Transform) && (tableOrViewSource == false)))
  851. {
  852. // add the mapping from destination transform to destination table
  853. repository.AddMapping(componentRepositoryID, tableRepositoryID);
  854. }
  855. }
  856. }
  857. private void ParseTSqlStatement(string statement, int connectionID, DTSPipelineComponentType dataFlowComponentType, bool tableOrViewSource, int componentRepositoryID)
  858. {
  859. SqlStatement toBeParsed = new SqlStatement();
  860. //toBeParsed.sqlString = statement;
  861. toBeParsed.quotedIdentifiers = true;
  862. if (toBeParsed.ParseString(statement))
  863. {
  864. foreach (string tableName in toBeParsed.getTableNames(true))
  865. {
  866. int tableID = repository.GetTable(connectionID, tableName);
  867. AddTableMappings(dataFlowComponentType, tableOrViewSource, componentRepositoryID, tableID);
  868. }
  869. foreach (string procedureName in toBeParsed.getProcedureNames(true))
  870. {
  871. int procID = repository.GetProcedure(connectionID, procedureName);
  872. AddTableMappings(dataFlowComponentType, tableOrViewSource, componentRepositoryID, procID);
  873. }
  874. foreach (string funcName in toBeParsed.getFunctionNames(true))
  875. {
  876. int funcID = repository.GetFunction(connectionID, funcName);
  877. AddTableMappings(dataFlowComponentType, tableOrViewSource, componentRepositoryID, funcID);
  878. }
  879. }
  880. else
  881. {
  882. string errorMessage = "The following messages where generated whilst parsing the sql statement\r\n" + statement + "\r\n";
  883. foreach (string error in toBeParsed.parseErrors)
  884. {
  885. errorMessage += error + "\r\n";
  886. }
  887. Console.WriteLine(errorMessage);
  888. }
  889. }
  890. private void EnumerateComponentConnections(Package package, IDTSComponentMetaData component, string objectType,
  891. DTSPipelineComponentType dataFlowComponentType, string tableOrViewName, bool tableOrViewSource, string queryDefinition, int componentRepositoryID)
  892. {
  893. foreach (IDTSRuntimeConnection runtimeConnection in component.RuntimeConnectionCollection)
  894. {
  895. // todo: what happens if this connection isn't available anymore
  896. if (package.Connections.Contains(runtimeConnection.ConnectionManagerID))
  897. {
  898. ConnectionManager connectionManager = package.Connections[runtimeConnection.ConnectionManagerID];
  899. string connectionManagerType = connectionManager.CreationName;
  900. int connectionID = repository.GetConnection(connectionManager.ConnectionString);
  901. string serverName = null;
  902. if (connectionID == -1)
  903. {
  904. connectionID = CreateConnection(connectionManager, out serverName);
  905. }
  906. if (!string.IsNullOrEmpty(queryDefinition))
  907. {
  908. try
  909. {
  910. ParseTSqlStatement(queryDefinition, connectionID, dataFlowComponentType, tableOrViewSource, componentRepositoryID);
  911. }
  912. catch (System.Exception err)
  913. {
  914. Console.WriteLine("The exception \r\n{0}\r\nwas raised against query \r\n{1}\r\nPlease report to http://sqlmetadata.codeplex.com/WorkItem/List.aspx\r\n", err.Message, queryDefinition);
  915. }
  916. /*
  917. var statements = new List<IStatement> { };
  918. try
  919. {
  920. statements = ParserFactory.Execute(queryDefinition);
  921. }
  922. catch (System.Exception err)
  923. {
  924. Console.WriteLine("The exception \r\n{0}\r\nwas raised against query \r\n{1}\r\nPlease report to http://sqlmetadata.codeplex.com/WorkItem/List.aspx\r\n", err.Message, queryDefinition);
  925. }
  926. foreach (IStatement statement in statements)
  927. {
  928. if (statement is Laan.Sql.Parser.Entities.SelectStatement)
  929. {
  930. ParseLaanSqlStatement(statement, connectionID, dataFlowComponentType, tableOrViewSource, componentRepositoryID, String.Empty);
  931. }
  932. if (statement is Laan.Sql.Parser.Entities.WithStatement)
  933. {
  934. ParseLaanSqlStatement((statement as Laan.Sql.Parser.Entities.WithStatement).CTERetrieve, connectionID, dataFlowComponentType, tableOrViewSource, componentRepositoryID, (statement as Laan.Sql.Parser.Entities.WithStatement).CTETable.Value);
  935. ParseLaanSqlStatement((statement as Laan.Sql.Parser.Entities.WithStatement).CTESelect, connectionID, dataFlowComponentType, tableOrViewSource, componentRepositoryID, (statement as Laan.Sql.Parser.Entities.WithStatement).CTETable.Value);
  936. }
  937. }
  938. */
  939. }
  940. else
  941. {
  942. int tableRepositoryID = -1;
  943. if (connectionManagerType == "ADO" || connectionManagerType == "OLEDB" ||
  944. connectionManagerType == "ADO.NET" || connectionManagerType == "ODBC")
  945. {
  946. if (!string.IsNullOrEmpty(tableOrViewName))
  947. {
  948. // add the table to the repository for each distinct connection
  949. tableRepositoryID = repository.GetTable(connectionID, tableOrViewName);
  950. }
  951. }
  952. else if (connectionManagerType == "FILE" || connectionManagerType == "FLATFILE" ||
  953. connectionManagerType == "EXCEL")
  954. {
  955. // add the table to the repository for each distinct connection
  956. tableRepositoryID = repository.GetFile(connectionManager.ConnectionString, "localhost");
  957. }
  958. AddTableMappings(dataFlowComponentType, tableOrViewSource, componentRepositoryID, tableRepositoryID);
  959. }
  960. }
  961. }
  962. // special case some components that don't use connection managers
  963. if (objectType == ClassIDs.XmlSource)
  964. {
  965. string xmlFile = GetStringComponentPropertyValue(component, "XMLData");
  966. int fileID = repository.GetFile(xmlFile, "localhost");
  967. repository.AddMapping(fileID, componentRepositoryID);
  968. xmlFile = GetStringComponentPropertyValue(component, "XMLSchemaDefinition");
  969. fileID = repository.GetFile(xmlFile, "localhost");
  970. repository.AddMapping(fileID, componentRepositoryID);
  971. }
  972. else if (objectType == ClassIDs.RawFileSource)
  973. {
  974. string rawFile = GetStringComponentPropertyValue(component, "FileName");
  975. if (rawFile == null)
  976. {
  977. rawFile = GetStringComponentPropertyValue(component, "FileNameVariable");
  978. }
  979. int fileID = repository.GetFile(rawFile, "localhost");
  980. repository.AddMapping(fileID, componentRepositoryID);
  981. }
  982. else if (objectType == ClassIDs.RawFileDest)
  983. {
  984. string rawFile = GetStringComponentPropertyValue(component, "FileName");
  985. if (rawFile == null)
  986. {
  987. rawFile = GetStringComponentPropertyValue(component, "FileNameVariable");
  988. }
  989. int fileID = repository.GetFile(rawFile, "localhost");
  990. repository.AddMapping(componentRepositoryID, fileID);
  991. }
  992. // add all the Columns as Attributes (just for fun?)
  993. // AddColumnNameAttributes(component, componentRepositoryID);
  994. }
  995. private int CreateConnection(ConnectionManager connectionManager, out string serverName)
  996. {
  997. // todo: is the root object of the connection the root repository object ID?
  998. string connectionType = connectionManager.CreationName;
  999. if (connectionType.IndexOf(':') > 0)
  1000. connectionType = connectionType.Substring(0, connectionType.IndexOf(':'));
  1001. int connectionID = AddConnectionObject(connectionManager.Name, connectionManager.Description, connectionType, repository.RootRepositoryObjectID);
  1002. // add the attributes of the connection as well
  1003. repository.AddAttribute(connectionID, Repository.Attributes.ConnectionString, connectionManager.ConnectionString);
  1004. // get the server name/initial catalog, etc.
  1005. string initialCatalog;
  1006. GetConnectionAttributes(connectionManager, out serverName, out initialCatalog);
  1007. if (string.IsNullOrEmpty(serverName) == false)
  1008. {
  1009. repository.AddAttribute(connectionID, Repository.Attributes.ConnectionServer, serverName);
  1010. }
  1011. if (string.IsNullOrEmpty(initialCatalog) == false)
  1012. {
  1013. repository.AddAttribute(connectionID, Repository.Attributes.ConnectionDatabase, initialCatalog);
  1014. }
  1015. return connectionID;
  1016. }
  1017. private void GetConnectionAttributes(ConnectionManager connectionManager, out string serverName, out string initialCatalog)
  1018. {
  1019. serverName = null;
  1020. initialCatalog = null;
  1021. try
  1022. {
  1023. if ((connectionManager.CreationName == "OLEDB") || (connectionManager.CreationName == "EXCEL"))
  1024. {
  1025. connectionStringBuilder.Clear();
  1026. connectionStringBuilder.ConnectionString = connectionManager.ConnectionString;
  1027. serverName = connectionStringBuilder.DataSource;
  1028. object outValue;
  1029. connectionStringBuilder.TryGetValue("Initial Catalog", out outValue);
  1030. if (outValue != null)
  1031. {
  1032. initialCatalog = outValue.ToString();
  1033. }
  1034. }
  1035. else
  1036. {
  1037. if (connectionManager.CreationName == "FLATFILE")
  1038. {
  1039. serverName = connectionManager.ConnectionString;
  1040. }
  1041. }
  1042. }
  1043. catch (System.Exception ex)
  1044. {
  1045. Console.WriteLine(string.Format("Error occurred: Could not completely parse connection string for '{0}': {1}", connectionManager.Name, ex.Message));
  1046. }
  1047. }
  1048. /// <summary>
  1049. /// enumerate all mappings that exist between a group of source and target components and write them out to the repository.
  1050. /// </summary>
  1051. /// <param name="pipeline"></param>
  1052. /// <param name="dataFlowRepositoryObjectID"></param>
  1053. /// <param name="componentIDToSourceRepositoryObjectMap"></param>
  1054. /// <param name="componentIDToTargetRepositoryObjectMap"></param>
  1055. private void EnumerateMappings(IDTSPipeline pipeline, int dataFlowRepositoryObjectID,
  1056. Dictionary<int, int> componentIDToSourceRepositoryObjectMap,
  1057. Dictionary<int, int> componentIDToRepositoryObjectMap)
  1058. {
  1059. Debug.Assert(dataFlowRepositoryObjectID > 0);
  1060. Debug.Assert(componentIDToSourceRepositoryObjectMap.Count > 0);
  1061. Debug.Assert(componentIDToRepositoryObjectMap.Count > 0);
  1062. Dictionary<int, bool> outputsAlreadyInvestigated = new Dictionary<int, bool>(); // key: outputID
  1063. foreach (int sourceComponentID in componentIDToSourceRepositoryObjectMap.Keys)
  1064. {
  1065. EnumerateMappingForSource(pipeline, dataFlowRepositoryObjectID, sourceComponentID,
  1066. componentIDToRepositoryObjectMap, outputsAlreadyInvestigated);
  1067. }
  1068. }
  1069. /// <summary>
  1070. /// Enumerate all mappings that exist between a source and a list of target components and write them out to the repository.
  1071. /// If there are multiple mappings between a source and target only one will be written out.
  1072. /// </summary>
  1073. /// <param name="pipeline"></param>
  1074. /// <param name="dataFlowRepositoryObjectID"></param>
  1075. /// <param name="sourceComponentID"></param>
  1076. /// <param name="componentIDToTargetRepositoryObjectMap"></param>
  1077. private void EnumerateMappingForSource(IDTSPipeline pipeline, int dataFlowRepositoryObjectID,
  1078. int sourceComponentID, Dictionary<int, int> componentIDToRepositoryObjectMap,
  1079. Dictionary<int, bool> outputsAlreadyInvestigated)
  1080. {
  1081. // get the component
  1082. IDTSComponentMetaData componentMetadata = pipeline.ComponentMetaDataCollection.GetObjectByID(sourceComponentID);
  1083. Debug.Assert(componentMetadata != null);
  1084. Queue<IDTSOutput> outputsToInvestigate = new Queue<IDTSOutput>();
  1085. // go through each output of the source.
  1086. foreach (IDTSOutput output in componentMetadata.OutputCollection)
  1087. {
  1088. outputsToInvestigate.Enqueue(output);
  1089. }
  1090. while (outputsToInvestigate.Count > 0)
  1091. {
  1092. IDTSOutput output = outputsToInvestigate.Dequeue();
  1093. int outputID = output.ID;
  1094. if (outputsAlreadyInvestigated.ContainsKey(outputID) == false)
  1095. {
  1096. outputsAlreadyInvestigated.Add(outputID, true);
  1097. // find out whethere the next component has one of the source IDs
  1098. IDTSComponentMetaData connectedComponent = GetConnectedComponentID(pipeline, output);
  1099. if (connectedComponent != null)
  1100. {
  1101. int connectedComponentID = connectedComponent.ID;
  1102. // if the target component doesn't exist in the repository already, add it now
  1103. int targetObjectRepositoryID = -1;
  1104. if (componentIDToRepositoryObjectMap.ContainsKey(connectedComponentID))
  1105. {
  1106. targetObjectRepositoryID = componentIDToRepositoryObjectMap[connectedComponentID];
  1107. }
  1108. else
  1109. {
  1110. targetObjectRepositoryID = AddObject(connectedComponent.Name, connectedComponent.Description, connectedComponent.ComponentClassID, dataFlowRepositoryObjectID);
  1111. componentIDToRepositoryObjectMap.Add(connectedComponentID, targetObjectRepositoryID);
  1112. }
  1113. Debug.Assert(componentIDToRepositoryObjectMap.ContainsKey(output.Component.ID));
  1114. int sourceObjectRepositoryID = componentIDToRepositoryObjectMap[output.Component.ID];
  1115. // create a mapping if it doesn't exist already
  1116. if (repository.DoesMappingExist(sourceObjectRepositoryID, targetObjectRepositoryID) == false)
  1117. {
  1118. repository.AddMapping(sourceObjectRepositoryID, targetObjectRepositoryID);
  1119. }
  1120. // otherwise add all outputs from the connected component as well
  1121. foreach (IDTSOutput outputToTraverse in connectedComponent.OutputCollection)
  1122. {
  1123. outputsToInvestigate.Enqueue(outputToTraverse);
  1124. }
  1125. }
  1126. }
  1127. }
  1128. }
  1129. /// <summary>
  1130. /// Traverses the output->input path (if there is one) and reports the component of the input.
  1131. /// </summary>
  1132. /// <param name="pipeline"></param>
  1133. /// <param name="output"></param>
  1134. /// <returns></returns>
  1135. private IDTSComponentMetaData GetConnectedComponentID(IDTSPipeline pipeline, IDTSOutput output)
  1136. {
  1137. int outputID = output.ID;
  1138. foreach (IDTSPath path in pipeline.PathCollection)
  1139. {
  1140. if (path.StartPoint.ID == outputID)
  1141. {
  1142. // got it
  1143. return path.EndPoint.Component;
  1144. }
  1145. }
  1146. // this output is dangling, not connected to anything else
  1147. return null;
  1148. }
  1149. /// <summary>
  1150. /// if the component is interesting, return that fact and the object type
  1151. /// </summary>
  1152. /// <param name="component">component to inspect</param>
  1153. /// <param name="objectTypeName">object type name</param>
  1154. /// <param name="componentType">Source/Destination/Transform</param>
  1155. /// <param name="domain">Repository.Domains.Relational or Repository.Domains.File</param>
  1156. /// <param name="tableOrViewName">table or view for relational domain</param>
  1157. /// <param name="tableOrViewSource">Whether the table/view (if specified) is a source or a destination</param>
  1158. /// <returns></returns>
  1159. private bool IsComponentInteresting(Package package, IDTSComponentMetaData component,
  1160. out string objectTypeName,
  1161. out DTSPipelineComponentType componentType,
  1162. out string domain,
  1163. out string tableOrViewName,
  1164. out bool tableOrViewSource,
  1165. out string queryDefinition)
  1166. {
  1167. objectTypeName = component.ComponentClassID;
  1168. domain = null;
  1169. tableOrViewName = null;
  1170. queryDefinition = null;
  1171. tableOrViewSource = false;
  1172. // for managed components, the type name is stored in a custom property
  1173. if (string.Compare(objectTypeName, ClassIDs.ManagedComponentWrapper, StringComparison.InvariantCultureIgnoreCase) == 0)
  1174. {
  1175. objectTypeName = component.CustomPropertyCollection["UserComponentTypeName"].Value.ToString();
  1176. }
  1177. if (pipelineComponentInfos.Contains(objectTypeName) == false)
  1178. {
  1179. throw new Exception(string.Format("Unknown component type encountered: {0}", objectTypeName));
  1180. }
  1181. PipelineComponentInfo pipelineComponentInfo = pipelineComponentInfos[objectTypeName];
  1182. Debug.Assert(pipelineComponentInfo != null);
  1183. componentType = pipelineComponentInfo.ComponentType;
  1184. string componentClassID = component.ComponentClassID;
  1185. if ((string.Equals(componentClassID, ClassIDs.OleDbSource))
  1186. || (string.Equals(componentClassID, ClassIDs.ExcelSource))
  1187. || (string.Equals(componentClassID, ClassIDs.OleDbDestination)))
  1188. {
  1189. domain = Repository.Domains.Relational;
  1190. GetOleDbComponentsInfo(package, component, ref tableOrViewName, ref queryDefinition);
  1191. tableOrViewSource = !(string.Equals(componentClassID, ClassIDs.OleDbDestination));
  1192. }
  1193. else if (string.Equals(componentClassID, ClassIDs.SqlDestination))
  1194. {
  1195. domain = Repository.Domains.Relational;
  1196. tableOrViewName = GetStringComponentPropertyValue(component, "BulkInsertTableName");
  1197. tableOrViewSource = false;
  1198. }
  1199. else if ((string.Equals(componentClassID, ClassIDs.FlatFileSource))
  1200. || (string.Equals(componentClassID, ClassIDs.FlatFileDest)))
  1201. {
  1202. domain = Repository.Domains.File;
  1203. // flat file connection is always 'interesting', but does not use table name, which was nulled out earlier anyways
  1204. }
  1205. else if (string.Equals(componentClassID, ClassIDs.Lookup))
  1206. {
  1207. domain = Repository.Domains.Relational;
  1208. queryDefinition = GetLookupInfo(component);
  1209. }
  1210. else if (string.Equals(componentClassID, ClassIDs.FuzzyLookup))
  1211. {
  1212. domain = Repository.Domains.Relational;
  1213. tableOrViewName = GetFuzzyLookupInfo(component);
  1214. tableOrViewSource = true;
  1215. }
  1216. else if (string.Equals(componentClassID, ClassIDs.OLEDBCommand))
  1217. {
  1218. queryDefinition = GetLookupInfo(component);
  1219. }
  1220. return (objectTypeName != null);
  1221. }
  1222. private static string GetFuzzyLookupInfo(IDTSComponentMetaData component)
  1223. {
  1224. return GetStringComponentPropertyValue(component, "ReferenceTableName");
  1225. }
  1226. /// <summary>
  1227. /// returns information about the query used by the lookup/fuzzy lookup components
  1228. /// </summary>
  1229. /// <param name="component"></param>
  1230. /// <param name="queryDefinition"></param>
  1231. private static string GetLookupInfo(IDTSComponentMetaData component)
  1232. {
  1233. return GetStringComponentPropertyValue(component, "SqlCommand");
  1234. }
  1235. private static string GetStringComponentPropertyValue(IDTSComponentMetaData component, string propertyName)
  1236. {
  1237. IDTSCustomProperty prop = null;
  1238. try
  1239. {
  1240. prop = component.CustomPropertyCollection[propertyName];
  1241. }
  1242. catch
  1243. {
  1244. // No Action here.
  1245. }
  1246. string value = null;
  1247. if (prop != null && prop.Value is string)
  1248. {
  1249. value = prop.Value.ToString();
  1250. }
  1251. return value;
  1252. }
  1253. /// <summary>
  1254. /// returns information about OleDb source, destination and Excel source.
  1255. /// </summary>
  1256. /// <param name="component"></param>
  1257. /// <param name="tableOrViewName"></param>
  1258. /// <param name="queryDefinition"></param>
  1259. private static void GetOleDbComponentsInfo(Package package, IDTSComponentMetaData component, ref string tableOrViewName, ref string queryDefinition)
  1260. {
  1261. IDTSCustomProperty prop = component.CustomPropertyCollection["AccessMode"];
  1262. string strVariableName;
  1263. tableOrViewName = "";
  1264. queryDefinition = "";
  1265. if (prop != null && prop.Value is int)
  1266. {
  1267. int accessMode = (int)prop.Value;
  1268. switch (accessMode)
  1269. {
  1270. case (int)AccessMode.AM_OPENROWSET:
  1271. tableOrViewName = GetStringComponentPropertyValue(component, "OpenRowset");
  1272. break;
  1273. case (int)AccessMode.AM_OPENROWSET_VARIABLE:
  1274. strVariableName = GetStringComponentPropertyValue(component, "OpenRowsetVariable");
  1275. tableOrViewName = GetVariable(package, strVariableName);
  1276. break;
  1277. case (int)AccessMode.AM_SQLCOMMAND:
  1278. queryDefinition = GetStringComponentPropertyValue(component, "SqlCommand");
  1279. break;
  1280. case (int)AccessMode.AM_SQLCOMMAND_VARIABLE:
  1281. strVariableName = GetStringComponentPropertyValue(component, "SqlCommandVariable");
  1282. if (strVariableName == null)
  1283. {
  1284. tableOrViewName = GetStringComponentPropertyValue(component, "OpenRowset");
  1285. }
  1286. else
  1287. {
  1288. queryDefinition = GetVariable(package, strVariableName);
  1289. }
  1290. break;
  1291. case (int)AccessMode.AM_OPENROWSET_FASTLOAD_VARIABLE:
  1292. strVariableName = GetStringComponentPropertyValue(component, "OpenRowsetVariable");
  1293. if (strVariableName == null)
  1294. {
  1295. tableOrViewName = string.Empty;
  1296. queryDefinition = string.Empty;
  1297. Console.WriteLine("Unexpected setup for OLEDB Fast Load from Variable. Table details not collected.\r\nPlease report to http://sqlmetadata.codeplex.com/WorkItem/List.aspx\r\nWith the SSIS Package if possible.");
  1298. }
  1299. else
  1300. {
  1301. tableOrViewName = GetVariable(package, strVariableName);
  1302. }
  1303. break;
  1304. default:
  1305. throw new Exception(string.Format("Invalid access mode {0}.", accessMode));
  1306. }
  1307. }
  1308. }
  1309. private static string GetVariable(Package package, string strVariableName)
  1310. {
  1311. if (strVariableName != null)
  1312. return package.Variables[strVariableName].Value.ToString();
  1313. else
  1314. return null;
  1315. }
  1316. private int AddConnectionObject(string name, string description, string type, int parentObjectID)
  1317. {
  1318. string id = connectionTypeToIDMap[type];
  1319. return repository.AddObject(name, description, id, parentObjectID);
  1320. }
  1321. /// <summary>
  1322. /// Add a new object to the repository
  1323. /// </summary>
  1324. /// <param name="name"></param>
  1325. /// <param name="description"></param>
  1326. /// <param name="objectType"></param>
  1327. /// <param name="parentObjectID"></param>
  1328. /// <returns></returns>
  1329. private int AddObject(string name, string description, string objectType, int parentObjectID)
  1330. {
  1331. if (!repository.IsTypeDefined(objectType))
  1332. {
  1333. // object type might be a progid instead of a clsid. We need to use clsids if one exists
  1334. bool isGuid = true;
  1335. Guid guid;
  1336. isGuid = Guid.TryParse(objectType, out guid);
  1337. if (isGuid == false)
  1338. {
  1339. try
  1340. {
  1341. uint result = NativeMethods.CLSIDFromProgID(objectType, out guid);
  1342. if (result == 0)
  1343. {
  1344. objectType = guid.ToString("B").ToUpper();
  1345. }
  1346. }
  1347. catch (System.Exception)
  1348. {
  1349. // ignore, use the original objectType
  1350. }
  1351. }
  1352. }
  1353. return repository.AddObject(name, description, objectType, parentObjectID);
  1354. }
  1355. }
  1356. }