PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/AddIns/DisplayBindings/Data/ICSharpCode.Data.SQLServer/SQLServerDatabaseDriver.cs

http://github.com/icsharpcode/SharpDevelop
C# | 522 lines | 479 code | 22 blank | 21 comment | 9 complexity | f08b07b34bd083d3f746c0dc0255ad97 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CPL-1.0, LGPL-2.1
  1. // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. #region Usings
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Linq;
  22. using System.Text;
  23. using System.Data;
  24. using System.Data.Sql;
  25. using ICSharpCode.Data.Core.Common;
  26. using ICSharpCode.Data.Core.Interfaces;
  27. using System.Collections.ObjectModel;
  28. using ICSharpCode.Data.Core.DatabaseObjects;
  29. using System.Data.SqlClient;
  30. using System.Collections.Specialized;
  31. using ICSharpCode.Data.Core.Enums;
  32. using System.Windows;
  33. using System.Windows.Threading;
  34. #endregion
  35. namespace ICSharpCode.Data.Core.DatabaseDrivers.SQLServer
  36. {
  37. public class SQLServerDatabaseDriver : DatabaseDriver<SQLServerDatasource>
  38. {
  39. #region Consts
  40. private const string _getTables = @"SELECT TABLE_SCHEMA, TABLE_NAME from INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND TABLE_NAME<>'dtproperties' ORDER BY TABLE_SCHEMA, TABLE_NAME";
  41. //http://community.sharpdevelop.net/forums/p/12955/37213.aspx#37213
  42. //remove this line clmns.is_column_set AS [IsColumnSet],
  43. private const string _getColumnsScript = @"DECLARE @tablename varchar(100) SET @tablename = N'{0}'
  44. SELECT
  45. clmns.column_id AS [ColumnId],
  46. clmns.name AS [Name],
  47. usrt.name AS [DataType],
  48. ISNULL(baset.name, N'') AS [SystemType],
  49. CAST(CASE WHEN baset.name IN (N'nchar', N'nvarchar') AND clmns.max_length <> -1 THEN
  50. clmns.max_length/2 ELSE 0 END AS INT) AS [Length],
  51. CAST(clmns.precision AS int) AS [NumericPrecision],
  52. clmns.default_object_id AS [DefaultObjectId],
  53. clmns.is_ansi_padded AS [IsAnsiPadded],
  54. clmns.is_computed AS [IsComputed],
  55. clmns.is_dts_replicated AS [IsDtsReplicated],
  56. clmns.is_filestream AS [IsFileStream],
  57. clmns.is_identity AS [IsIdentity],
  58. clmns.is_merge_published AS [IsMergePublished],
  59. clmns.is_non_sql_subscribed AS [IsNonSqlSubscribed],
  60. clmns.is_nullable AS [IsNullable],
  61. clmns.is_replicated AS [IsReplicated],
  62. clmns.is_rowguidcol AS [IsRowGuidCol],
  63. clmns.is_sparse AS [IsSparse],
  64. clmns.is_xml_document AS [IsXmlDocument],
  65. clmns.object_id AS [ObjectId],
  66. clmns.rule_object_id AS [RuleObjectId],
  67. clmns.scale AS [Scale],
  68. clmns.system_type_id AS [SystemTypeId],
  69. clmns.user_type_id AS [UserTypeId],
  70. clmns.xml_collection_id AS [XMLCollectionId],
  71. CAST(
  72. CASE WHEN (
  73. SELECT c.name AS ColumnName
  74. FROM sys.key_constraints AS k
  75. JOIN sys.tables AS t ON t.object_id = k.parent_object_id
  76. JOIN sys.schemas AS s ON s.schema_id = t.schema_id
  77. JOIN sys.index_columns AS ic ON ic.object_id = t.object_id AND ic.index_id = k.unique_index_id
  78. JOIN sys.columns AS c ON c.object_id = t.object_id AND c.column_id = ic.column_id
  79. WHERE t.name=@tablename AND c.name = clmns.name)
  80. IS NULL THEN 0 ELSE 1 END AS BIT) AS [IsPrimaryKey]
  81. FROM
  82. sys.{1} AS tbl
  83. INNER JOIN sys.all_columns AS clmns ON clmns.object_id=tbl.object_id
  84. LEFT OUTER JOIN sys.types AS usrt ON usrt.user_type_id = clmns.user_type_id
  85. LEFT OUTER JOIN sys.types AS baset ON baset.user_type_id = clmns.system_type_id and
  86. baset.user_type_id = baset.system_type_id
  87. WHERE
  88. (tbl.name=@tablename and SCHEMA_NAME(tbl.schema_id)=N'{2}')
  89. ORDER BY
  90. clmns.column_id ASC";
  91. private const string _getConstraintsScript = @"SELECT
  92. FKTable = FK.TABLE_NAME,
  93. FKColumn = CU.COLUMN_NAME,
  94. PKTable = PK.TABLE_NAME,
  95. PKColumn = PT.COLUMN_NAME,
  96. ConstraintName = C.CONSTRAINT_NAME
  97. FROM
  98. INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
  99. INNER JOIN
  100. INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
  101. ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
  102. INNER JOIN
  103. INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
  104. ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
  105. INNER JOIN
  106. INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
  107. ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
  108. INNER JOIN
  109. (
  110. SELECT
  111. i1.TABLE_NAME, i2.COLUMN_NAME
  112. FROM
  113. INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
  114. INNER JOIN
  115. INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2
  116. ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
  117. WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
  118. ) PT
  119. ON PT.TABLE_NAME = PK.TABLE_NAME
  120. -- optional:
  121. ORDER BY
  122. 1,2,3,4";
  123. private const string _getViews = @"SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='VIEW' AND TABLE_NAME<>'dtproperties' ORDER BY TABLE_SCHEMA, TABLE_NAME";
  124. private const string _getViewDefiningQuery = @"EXEC sp_helptext '{0}'";
  125. private const string _getProcedures = "SELECT ROUTINE_NAME, ROUTINE_SCHEMA, ROUTINE_BODY, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE'";
  126. private const string _getProcedureParameters = @"SELECT PARAMETER_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, PARAMETER_MODE, IS_RESULT FROM information_schema.PARAMETERS WHERE SPECIFIC_NAME = '{0}' AND SPECIFIC_SCHEMA = '{1}' AND SPECIFIC_CATALOG = '{2}'";
  127. #endregion
  128. public SQLServerDatabaseDriver()
  129. {
  130. Datasources = new DatabaseObjectsCollection<SQLServerDatasource>(null);
  131. }
  132. public override string Name
  133. {
  134. get { return "MS SQL Server"; }
  135. }
  136. public override string ProviderName
  137. {
  138. get { return "System.Data.SqlClient"; }
  139. }
  140. public override string ODBCProviderName
  141. {
  142. // SqlServer 2012
  143. get { return "SQLNCLI10.1"; }
  144. // get { return "SQLNCLI11.0"; }
  145. }
  146. public override void PopulateDatasources()
  147. {
  148. DatabaseObjectsCollection<SQLServerDatasource> datasources = new DatabaseObjectsCollection<SQLServerDatasource>(null);
  149. DataTable dt = SqlDataSourceEnumerator.Instance.GetDataSources();
  150. foreach (DataRow dr in dt.Rows)
  151. {
  152. string serverName = dr["ServerName"].ToString().Trim().ToUpper();
  153. string instanceName = null;
  154. Version version = null;
  155. if (dr["InstanceName"] != null && dr["InstanceName"] != DBNull.Value)
  156. instanceName = dr["InstanceName"].ToString().Trim().ToUpper();
  157. if (dr["Version"] != null && dr["Version"] != DBNull.Value)
  158. version = new Version(dr["Version"].ToString().Trim());
  159. SQLServerDatasource datasource = new SQLServerDatasource(this) { Name = serverName };
  160. datasource.ProviderManifestToken = GetManifestToken(version);
  161. if (!String.IsNullOrEmpty(instanceName))
  162. datasource.Name += "\\" + instanceName;
  163. datasources.Add(datasource);
  164. }
  165. Datasources = datasources;
  166. }
  167. string GetManifestToken(Version version)
  168. {
  169. string manifestToken;
  170. if (!IsVersionSupported(version, out manifestToken))
  171. throw new NotSupportedException(string.Format("Version '{0}' is not supported!", version == null ? "unknown" : version.ToString()));
  172. return manifestToken;
  173. }
  174. bool IsVersionSupported(Version version, out string manifestToken)
  175. {
  176. manifestToken = "";
  177. if (version == null)
  178. return false;
  179. switch (version.Major) {
  180. case 8:
  181. manifestToken = "2000";
  182. return false;
  183. case 9:
  184. manifestToken = "2005";
  185. return true;
  186. case 10:
  187. if (version.Minor == 5)
  188. manifestToken = "2008 R2";
  189. else
  190. manifestToken = "2008";
  191. return true;
  192. case 11:
  193. manifestToken = "2012";
  194. return true;
  195. }
  196. return false;
  197. }
  198. public override void PopulateDatabases(IDatasource datasource)
  199. {
  200. DatabaseObjectsCollection<IDatabase> databases = new DatabaseObjectsCollection<IDatabase>(datasource);
  201. SqlConnection sqlConnection = null;
  202. sqlConnection = new SqlConnection();
  203. sqlConnection.ConnectionString = datasource.ConnectionString;
  204. try
  205. {
  206. sqlConnection.Open();
  207. }
  208. catch (SqlException ex)
  209. {
  210. switch (ex.Number)
  211. {
  212. case 2:
  213. case 3:
  214. case 53:
  215. Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
  216. {
  217. Datasources.Remove(datasource as SQLServerDatasource);
  218. }));
  219. break;
  220. default:
  221. break;
  222. }
  223. throw ex;
  224. }
  225. Version version = new Version(sqlConnection.ServerVersion);
  226. datasource.ProviderManifestToken = GetManifestToken(version);
  227. string sql = string.Empty;
  228. if (version.Major >= 9)
  229. sql = "use master; select name from sys.databases order by name";
  230. else
  231. sql = "use master; select name from sysdatabases order by name";
  232. SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection);
  233. sqlCommand.CommandTimeout = 20;
  234. SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
  235. while (sqlDataReader.Read())
  236. {
  237. databases.Add(new Database(datasource) { Name = sqlDataReader["name"].ToString() });
  238. }
  239. sqlDataReader.Close();
  240. datasource.Databases = databases;
  241. if (sqlConnection != null && sqlConnection.State == ConnectionState.Open)
  242. sqlConnection.Close();
  243. }
  244. private void LoadColumns(SqlConnection sqlConnection, ITable table, TableType tableType)
  245. {
  246. string tableTypeName = "tables";
  247. if (tableType == TableType.View)
  248. tableTypeName = "views";
  249. using (SqlDataAdapter dataAdapter =
  250. new SqlDataAdapter(string.Format(_getColumnsScript, table.TableName, tableTypeName, table.SchemaName), sqlConnection))
  251. {
  252. DataTable dtColumns = new DataTable("Columns");
  253. dataAdapter.Fill(dtColumns);
  254. for (int j = 0; j < dtColumns.Rows.Count; j++)
  255. {
  256. Column column = new Column(table);
  257. column.ColumnId = (int)dtColumns.Rows[j]["ColumnId"];
  258. column.Name = (string)dtColumns.Rows[j]["Name"];
  259. column.DataType = (string)dtColumns.Rows[j]["DataType"];
  260. column.SystemType = (string)dtColumns.Rows[j]["SystemType"];
  261. column.Length = Convert.ToInt32(dtColumns.Rows[j]["Length"]);
  262. if (column.Length == -1)
  263. {
  264. switch (column.DataType.ToLower())
  265. {
  266. case "varchar":
  267. case "nvarchar":
  268. column.DataType += "(max)";
  269. break;
  270. default:
  271. break;
  272. }
  273. switch (column.SystemType.ToLower())
  274. {
  275. case "varchar":
  276. case "nvarchar":
  277. column.SystemType += "(max)";
  278. break;
  279. default:
  280. break;
  281. }
  282. }
  283. column.Precision = Convert.ToInt32(dtColumns.Rows[j]["NumericPrecision"]);
  284. column.Scale = Convert.ToInt32(dtColumns.Rows[j]["Scale"]);
  285. column.IsIdentity = (bool)dtColumns.Rows[j]["IsIdentity"];
  286. column.IsNullable = (bool)dtColumns.Rows[j]["IsNullable"];
  287. column.IsPrimaryKey = (bool)dtColumns.Rows[j]["IsPrimaryKey"];
  288. table.Items.Add(column);
  289. }
  290. }
  291. }
  292. public override DatabaseObjectsCollection<ITable> LoadTables(IDatabase database)
  293. {
  294. DatabaseObjectsCollection<ITable> tables = new DatabaseObjectsCollection<ITable>(database);
  295. SqlConnection sqlConnection = new SqlConnection(database.ConnectionString);
  296. using (SqlDataAdapter da = new SqlDataAdapter(_getConstraintsScript, sqlConnection))
  297. {
  298. DataTable dtConstraints = new DataTable("Constraints");
  299. da.Fill(dtConstraints);
  300. for (int i = 0; i < dtConstraints.Rows.Count; i++)
  301. {
  302. string constraintName = (string)dtConstraints.Rows[i]["ConstraintName"];
  303. IConstraint constraint = database.Constraints.FirstOrDefault(c => c.Name == constraintName);
  304. if (constraint == null)
  305. {
  306. constraint = new ICSharpCode.Data.Core.DatabaseObjects.Constraint();
  307. constraint.Name = constraintName;
  308. constraint.FKTableName = (string)dtConstraints.Rows[i]["FKTable"];
  309. constraint.PKTableName = (string)dtConstraints.Rows[i]["PKTable"];
  310. database.Constraints.Add(constraint);
  311. }
  312. constraint.FKColumnNames.Add((string)dtConstraints.Rows[i]["FKColumn"]);
  313. constraint.PKColumnNames.Add((string)dtConstraints.Rows[i]["PKColumn"]);
  314. }
  315. }
  316. using (SqlDataAdapter da = new SqlDataAdapter(_getTables, sqlConnection))
  317. {
  318. DataTable dtTables = new DataTable("Tables");
  319. da.Fill(dtTables);
  320. for (int i = 0; i < dtTables.Rows.Count; i++)
  321. {
  322. string schemaName = (string)dtTables.Rows[i]["TABLE_SCHEMA"];
  323. string tableName = (string)dtTables.Rows[i]["TABLE_NAME"];
  324. Table table = new Table() { SchemaName = schemaName, TableName = tableName };
  325. LoadColumns(sqlConnection, table, TableType.Table);
  326. table.Constraints = database.Constraints.Where(constraint => constraint.FKTableName == tableName).ToDatabaseObjectsCollection(table);
  327. tables.Add(table);
  328. }
  329. }
  330. return tables;
  331. }
  332. public override DatabaseObjectsCollection<IView> LoadViews(IDatabase database)
  333. {
  334. DatabaseObjectsCollection<IView> views = new DatabaseObjectsCollection<IView>(database);
  335. SqlConnection sqlConnection = new SqlConnection(database.ConnectionString);
  336. using (SqlDataAdapter da = new SqlDataAdapter(_getViews, sqlConnection))
  337. {
  338. DataTable dtViews = new DataTable("Views");
  339. da.Fill(dtViews);
  340. for (int i = 0; i < dtViews.Rows.Count; i++)
  341. {
  342. string schemaName = (string)dtViews.Rows[i]["TABLE_SCHEMA"];
  343. string viewName = (string)dtViews.Rows[i]["TABLE_NAME"];
  344. View view = new View() { SchemaName = schemaName, TableName = viewName, Query = LoadViewQuery(sqlConnection, schemaName, viewName) };
  345. LoadColumns(sqlConnection, view, TableType.View);
  346. views.Add(view);
  347. }
  348. }
  349. return views;
  350. }
  351. private string LoadViewQuery(SqlConnection sqlConnection, string schemaName, string tableName)
  352. {
  353. string definingQuery = string.Empty;
  354. using (SqlDataAdapter dataAdapter =
  355. new SqlDataAdapter(string.Format(_getViewDefiningQuery, schemaName + "." + tableName), sqlConnection))
  356. {
  357. DataTable dtQuery = new DataTable("Text");
  358. dataAdapter.Fill(dtQuery);
  359. for (int i = 0; i < dtQuery.Rows.Count; i++)
  360. {
  361. definingQuery += (string)dtQuery.Rows[i]["Text"];
  362. }
  363. }
  364. return definingQuery;
  365. }
  366. public override DatabaseObjectsCollection<IProcedure> LoadProcedures(IDatabase database)
  367. {
  368. DatabaseObjectsCollection<IProcedure> procedures = new DatabaseObjectsCollection<IProcedure>(database);
  369. SqlConnection sqlConnection = new SqlConnection(database.ConnectionString);
  370. using (SqlDataAdapter da = new SqlDataAdapter(_getProcedures, sqlConnection))
  371. {
  372. DataTable dtProcedures = new DataTable("Procedures");
  373. da.Fill(dtProcedures);
  374. for (int i = 0; i < dtProcedures.Rows.Count; i++)
  375. {
  376. Procedure procedure = new Procedure();
  377. procedure.Name = (string)dtProcedures.Rows[i]["ROUTINE_NAME"];
  378. procedure.SchemaName = (string)dtProcedures.Rows[i]["ROUTINE_SCHEMA"];
  379. if (dtProcedures.Rows[i]["DATA_TYPE"] != DBNull.Value)
  380. procedure.DataType = (string)dtProcedures.Rows[i]["DATA_TYPE"];
  381. if (dtProcedures.Rows[i]["CHARACTER_MAXIMUM_LENGTH"] != DBNull.Value)
  382. procedure.Length = Convert.ToInt32(dtProcedures.Rows[i]["CHARACTER_MAXIMUM_LENGTH"]);
  383. if (procedure.Length == -1)
  384. {
  385. switch (procedure.DataType.ToLower())
  386. {
  387. case "varchar":
  388. case "nvarchar":
  389. procedure.DataType += "(max)";
  390. break;
  391. default:
  392. break;
  393. }
  394. }
  395. string procedureType = (string)dtProcedures.Rows[i]["ROUTINE_BODY"];
  396. if (procedureType == "SQL")
  397. procedure.ProcedureType = ProcedureType.SQL;
  398. else
  399. procedure.ProcedureType = ProcedureType.External;
  400. procedure.Items = new DatabaseObjectsCollection<IProcedureParameter>(procedure);
  401. DatabaseObjectsCollection<IProcedureParameter> procedureParameters = new DatabaseObjectsCollection<IProcedureParameter>(procedure);
  402. da.SelectCommand = new SqlCommand(string.Format(_getProcedureParameters, procedure.Name, procedure.SchemaName, database.Name), sqlConnection);
  403. DataTable dtProcedureParameters = new DataTable("ProcedureParameters");
  404. da.Fill(dtProcedureParameters);
  405. for (int j = 0; j < dtProcedureParameters.Rows.Count; j++)
  406. {
  407. if (string.IsNullOrEmpty((string)dtProcedureParameters.Rows[j]["PARAMETER_NAME"]) &&
  408. (string)dtProcedureParameters.Rows[j]["IS_RESULT"] == "YES") // = ReturnValue
  409. continue;
  410. ProcedureParameter procedureParameter = new ProcedureParameter();
  411. procedureParameter.Name = (string)dtProcedureParameters.Rows[j]["PARAMETER_NAME"];
  412. if (procedureParameter.Name.StartsWith("@"))
  413. procedureParameter.Name = procedureParameter.Name.Substring(1);
  414. if (dtProcedureParameters.Rows[j]["DATA_TYPE"] != DBNull.Value)
  415. procedureParameter.DataType = (string)dtProcedureParameters.Rows[j]["DATA_TYPE"];
  416. if (dtProcedureParameters.Rows[j]["CHARACTER_MAXIMUM_LENGTH"] != DBNull.Value)
  417. procedureParameter.Length = Convert.ToInt32(dtProcedureParameters.Rows[j]["CHARACTER_MAXIMUM_LENGTH"]);
  418. string parameterMode = (string)dtProcedureParameters.Rows[j]["PARAMETER_MODE"];
  419. if (parameterMode == "IN")
  420. procedureParameter.ParameterMode = ParameterMode.In;
  421. else if (parameterMode == "OUT")
  422. procedureParameter.ParameterMode = ParameterMode.Out;
  423. else
  424. procedureParameter.ParameterMode = ParameterMode.InOut;
  425. procedure.Items.Add(procedureParameter);
  426. }
  427. procedures.Add(procedure);
  428. }
  429. }
  430. return procedures;
  431. }
  432. }
  433. }