// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) #region Usings using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.Sql; using ICSharpCode.Data.Core.Common; using ICSharpCode.Data.Core.Interfaces; using System.Collections.ObjectModel; using ICSharpCode.Data.Core.DatabaseObjects; using System.Data.SqlClient; using System.Collections.Specialized; using ICSharpCode.Data.Core.Enums; using System.Threading; #endregion namespace ICSharpCode.Data.Core.DatabaseDrivers.SQLServer { public class SQLServerDatabaseDriver : DatabaseDriver { #region Consts 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"; //http://community.sharpdevelop.net/forums/p/12955/37213.aspx#37213 //remove this line clmns.is_column_set AS [IsColumnSet], private const string _getColumnsScript_SQL_2000 = @"select clmns.colid AS [ColumnId], clmns.name AS [Name], usrt.name as [DataType], ISNULL(usrt.name, N'') AS [SystemType], CAST(CASE WHEN usrt.name IN (N'nchar', N'nvarchar') AND clmns.length <> -1 THEN clmns.length/2 ELSE 0 END AS INT) AS [Length], CAST(clmns.xprec AS int) AS [NumericPrecision], columnproperty(object_id('[{2}].[{0}]'),clmns.name,'IsIdentity') as [IsIdentity], clmns.isnullable AS [IsNullable], clmns.xscale AS [Scale], case when exists(SELECT 1 FROM sysobjects where xtype='PK' and parent_obj=clmns.id and name in ( SELECT name FROM sysindexes WHERE indid in( SELECT indid FROM sysindexkeys WHERE id = clmns.id AND colid=clmns.colid))) then 1 else 0 end as [IsPrimaryKey] from syscolumns as clmns inner join systypes as usrt ON usrt.xusertype = clmns.xusertype where id = (select id from sysobjects where name='{0}') order by [ColumnId]"; private const string _getColumnsScript = @"DECLARE @tablename varchar(100) SET @tablename = N'{0}' SELECT clmns.column_id AS [ColumnId], clmns.name AS [Name], usrt.name AS [DataType], ISNULL(baset.name, N'') AS [SystemType], CAST(CASE WHEN baset.name IN (N'nchar', N'nvarchar') AND clmns.max_length <> -1 THEN clmns.max_length/2 ELSE 0 END AS INT) AS [Length], CAST(clmns.precision AS int) AS [NumericPrecision], clmns.default_object_id AS [DefaultObjectId], clmns.is_ansi_padded AS [IsAnsiPadded], clmns.is_computed AS [IsComputed], clmns.is_dts_replicated AS [IsDtsReplicated], clmns.is_filestream AS [IsFileStream], clmns.is_identity AS [IsIdentity], clmns.is_merge_published AS [IsMergePublished], clmns.is_non_sql_subscribed AS [IsNonSqlSubscribed], clmns.is_nullable AS [IsNullable], clmns.is_replicated AS [IsReplicated], clmns.is_rowguidcol AS [IsRowGuidCol], clmns.is_xml_document AS [IsXmlDocument], clmns.object_id AS [ObjectId], clmns.rule_object_id AS [RuleObjectId], clmns.scale AS [Scale], clmns.system_type_id AS [SystemTypeId], clmns.user_type_id AS [UserTypeId], clmns.xml_collection_id AS [XMLCollectionId], CAST( CASE WHEN ( SELECT c.name AS ColumnName FROM sys.key_constraints AS k JOIN sys.tables AS t ON t.object_id = k.parent_object_id JOIN sys.schemas AS s ON s.schema_id = t.schema_id JOIN sys.index_columns AS ic ON ic.object_id = t.object_id AND ic.index_id = k.unique_index_id JOIN sys.columns AS c ON c.object_id = t.object_id AND c.column_id = ic.column_id WHERE t.name=@tablename AND c.name = clmns.name) IS NULL THEN 0 ELSE 1 END AS BIT) AS [IsPrimaryKey] FROM sys.{1} AS tbl INNER JOIN sys.all_columns AS clmns ON clmns.object_id=tbl.object_id LEFT OUTER JOIN sys.types AS usrt ON usrt.user_type_id = clmns.user_type_id LEFT OUTER JOIN sys.types AS baset ON baset.user_type_id = clmns.system_type_id and baset.user_type_id = baset.system_type_id WHERE (tbl.name=@tablename and SCHEMA_NAME(tbl.schema_id)=N'{2}') ORDER BY clmns.column_id ASC"; private const string _getConstraintsScript = @"SELECT FKTable = FK.TABLE_NAME, FKColumn = CU.COLUMN_NAME, PKTable = PK.TABLE_NAME, PKColumn = PT.COLUMN_NAME, ConstraintName = C.CONSTRAINT_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME INNER JOIN ( SELECT i1.TABLE_NAME, i2.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' ) PT ON PT.TABLE_NAME = PK.TABLE_NAME -- optional: ORDER BY 1,2,3,4"; 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"; private const string _getViewDefiningQuery = @"EXEC sp_helptext '{0}'"; private const string _getProcedures = "SELECT ROUTINE_NAME, ROUTINE_SCHEMA, ROUTINE_BODY, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE'"; 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}'"; #endregion public SQLServerDatabaseDriver() { Datasources = new DatabaseObjectsCollection(null); } public override string Name { get { return "MS SQL Server"; } } public override string ProviderName { get { return "System.Data.SqlClient"; } } public override string ODBCProviderName { get { return "SQLNCLI10.1"; } } public override void PopulateDatasources() { DatabaseObjectsCollection datasources = new DatabaseObjectsCollection(null); DataTable dt = SqlDataSourceEnumerator.Instance.GetDataSources(); foreach (DataRow dr in dt.Rows) { string serverName = dr["ServerName"].ToString().Trim().ToUpper(); string instanceName = null; Version version = null; if (dr["InstanceName"] != null && dr["InstanceName"] != DBNull.Value) instanceName = dr["InstanceName"].ToString().Trim().ToUpper(); if (dr["Version"] != null && dr["Version"] != DBNull.Value) version = new Version(dr["Version"].ToString().Trim()); SQLServerDatasource datasource = new SQLServerDatasource(this) { Name = serverName }; datasource.ProviderManifestToken = GetManifestToken(version); if (!String.IsNullOrEmpty(instanceName)) datasource.Name += "\\" + instanceName; datasources.Add(datasource); } Datasources = datasources; } string GetManifestToken(Version version) { string manifestToken; if (!IsVersionSupported(version, out manifestToken)) throw new NotSupportedException(string.Format("Version '{0}' is not supported!", version == null ? "unknown" : version.ToString())); return manifestToken; } bool IsVersionSupported(Version version, out string manifestToken) { manifestToken = ""; if (version == null) return false; switch (version.Major) { case 8: manifestToken = "2000"; return true; case 9: manifestToken = "2005"; return true; case 10: if (version.Minor == 5) manifestToken = "2008 R2"; else manifestToken = "2008"; return true; case 11: manifestToken = "2012"; return true; } return false; } public override void PopulateDatabases(IDatasource datasource) { DatabaseObjectsCollection databases = new DatabaseObjectsCollection(datasource); SqlConnection sqlConnection = null; sqlConnection = new SqlConnection(); sqlConnection.ConnectionString = datasource.ConnectionString; try { sqlConnection.Open(); } catch (SqlException ex) { switch (ex.Number) { case 2: case 3: case 53: new Thread(new ThreadStart(delegate() { Datasources.Remove(datasource as SQLServerDatasource); })).Start(); break; default: break; } ICSharpCode.Core.LoggingService.Error(ex.Message); ICSharpCode.Core.LoggingService.Error(ex.StackTrace); return; //throw ex; } Version version = new Version(sqlConnection.ServerVersion); datasource.ProviderManifestToken = GetManifestToken(version); string sql = string.Empty; if (version.Major >= 9) sql = "use master; select name from sys.databases order by name"; else sql = "use master; select name from sysdatabases order by name"; SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection); sqlCommand.CommandTimeout = 180; SqlDataReader sqlDataReader = sqlCommand.ExecuteReader(); while (sqlDataReader.Read()) { //databases.Add(new Database(datasource) { Name = sqlDataReader["name"].ToString() }); databases.Add(new Database(datasource) { Name = sqlDataReader["name"].ToString(), ConnectionString = datasource.ConnectionString + ";Initial Catalog=" + sqlDataReader["name"].ToString() }); } sqlDataReader.Close(); datasource.Databases = databases; if (sqlConnection != null && sqlConnection.State == ConnectionState.Open) sqlConnection.Close(); } private void LoadColumns2(string version, SqlConnection sqlConnection, ITable table, TableType tableType) { //database.Datasource.ProviderManifestToken string tableTypeName = "tables"; if (tableType == TableType.View) tableTypeName = "views"; string getColumnsScript = _getColumnsScript; if (version == "2000") { getColumnsScript = _getColumnsScript_SQL_2000; } using (SqlDataAdapter dataAdapter = new SqlDataAdapter(string.Format(getColumnsScript, table.TableName, tableTypeName, table.SchemaName), sqlConnection)) { DataTable dtColumns = new DataTable("Columns"); dataAdapter.Fill(dtColumns); for (int j = 0; j < dtColumns.Rows.Count; j++) { Column column = new Column(table); column.ColumnId = Convert.ToInt32(dtColumns.Rows[j]["ColumnId"]); column.Name = (string)dtColumns.Rows[j]["Name"]; column.DataType = (string)dtColumns.Rows[j]["DataType"]; column.SystemType = (string)dtColumns.Rows[j]["SystemType"]; column.Length = Convert.ToInt32(dtColumns.Rows[j]["Length"]); if (column.Length == -1) { switch (column.DataType.ToLower()) { case "varchar": case "nvarchar": column.DataType += "(max)"; break; default: break; } switch (column.SystemType.ToLower()) { case "varchar": case "nvarchar": column.SystemType += "(max)"; break; default: break; } } column.Precision = Convert.ToInt32(dtColumns.Rows[j]["NumericPrecision"]); column.Scale = Convert.ToInt32(dtColumns.Rows[j]["Scale"]); column.IsIdentity = Convert.ToBoolean(dtColumns.Rows[j]["IsIdentity"]); column.IsNullable = Convert.ToBoolean(dtColumns.Rows[j]["IsNullable"]); column.IsPrimaryKey = Convert.ToBoolean(dtColumns.Rows[j]["IsPrimaryKey"]); table.Items.Add(column); } } } private void LoadColumns(SqlConnection sqlConnection, ITable table, TableType tableType) { //datasource.ProviderManifestToken string tableTypeName = "tables"; if (tableType == TableType.View) tableTypeName = "views"; using (SqlDataAdapter dataAdapter = new SqlDataAdapter(string.Format(_getColumnsScript, table.TableName, tableTypeName, table.SchemaName), sqlConnection)) { DataTable dtColumns = new DataTable("Columns"); dataAdapter.Fill(dtColumns); for (int j = 0; j < dtColumns.Rows.Count; j++) { Column column = new Column(table); column.ColumnId = (int)dtColumns.Rows[j]["ColumnId"]; column.Name = (string)dtColumns.Rows[j]["Name"]; column.DataType = (string)dtColumns.Rows[j]["DataType"]; column.SystemType = (string)dtColumns.Rows[j]["SystemType"]; column.Length = Convert.ToInt32(dtColumns.Rows[j]["Length"]); if (column.Length == -1) { switch (column.DataType.ToLower()) { case "varchar": case "nvarchar": column.DataType += "(max)"; break; default: break; } switch (column.SystemType.ToLower()) { case "varchar": case "nvarchar": column.SystemType += "(max)"; break; default: break; } } column.Precision = Convert.ToInt32(dtColumns.Rows[j]["NumericPrecision"]); column.Scale = Convert.ToInt32(dtColumns.Rows[j]["Scale"]); column.IsIdentity = (bool)dtColumns.Rows[j]["IsIdentity"]; column.IsNullable = (bool)dtColumns.Rows[j]["IsNullable"]; column.IsPrimaryKey = (bool)dtColumns.Rows[j]["IsPrimaryKey"]; table.Items.Add(column); } } } public override DatabaseObjectsCollection LoadTables(IDatabase database) { DatabaseObjectsCollection tables = new DatabaseObjectsCollection(database); SqlConnection sqlConnection = new SqlConnection(database.ConnectionString); using (SqlDataAdapter da = new SqlDataAdapter(_getConstraintsScript, sqlConnection)) { DataTable dtConstraints = new DataTable("Constraints"); da.Fill(dtConstraints); for (int i = 0; i < dtConstraints.Rows.Count; i++) { string constraintName = (string)dtConstraints.Rows[i]["ConstraintName"]; if (database.Constraints.Count > 0) { IConstraint constraint = database.Constraints.FirstOrDefault(c => c.Name == constraintName); if (constraint == null) { constraint = new ICSharpCode.Data.Core.DatabaseObjects.Constraint(); constraint.Name = constraintName; constraint.FKTableName = (string)dtConstraints.Rows[i]["FKTable"]; constraint.PKTableName = (string)dtConstraints.Rows[i]["PKTable"]; database.Constraints.Add(constraint); } constraint.FKColumnNames.Add((string)dtConstraints.Rows[i]["FKColumn"]); constraint.PKColumnNames.Add((string)dtConstraints.Rows[i]["PKColumn"]); } } } using (SqlDataAdapter da = new SqlDataAdapter(_getTables, sqlConnection)) { DataTable dtTables = new DataTable("Tables"); da.Fill(dtTables); for (int i = 0; i < dtTables.Rows.Count; i++) { string schemaName = (string)dtTables.Rows[i]["TABLE_SCHEMA"]; string tableName = (string)dtTables.Rows[i]["TABLE_NAME"]; Table table = new Table() { SchemaName = schemaName, TableName = tableName }; //LoadColumns(sqlConnection, table, TableType.Table); LoadColumns2(database.Datasource.ProviderManifestToken, sqlConnection, table, TableType.Table); table.Constraints = database.Constraints.Where(constraint => constraint.FKTableName == tableName).ToDatabaseObjectsCollection(table); tables.Add(table); } } return tables; } public override DatabaseObjectsCollection LoadViews(IDatabase database) { DatabaseObjectsCollection views = new DatabaseObjectsCollection(database); SqlConnection sqlConnection = new SqlConnection(database.ConnectionString); using (SqlDataAdapter da = new SqlDataAdapter(_getViews, sqlConnection)) { DataTable dtViews = new DataTable("Views"); da.Fill(dtViews); for (int i = 0; i < dtViews.Rows.Count; i++) { string schemaName = (string)dtViews.Rows[i]["TABLE_SCHEMA"]; string viewName = (string)dtViews.Rows[i]["TABLE_NAME"]; View view = new View() { SchemaName = schemaName, TableName = viewName, Query = LoadViewQuery(sqlConnection, schemaName, viewName) }; //LoadColumns(sqlConnection, view, TableType.View); LoadColumns2(database.Datasource.ProviderManifestToken, sqlConnection, view, TableType.View); views.Add(view); } } return views; } private string LoadViewQuery(SqlConnection sqlConnection, string schemaName, string tableName) { string definingQuery = string.Empty; using (SqlDataAdapter dataAdapter = new SqlDataAdapter(string.Format(_getViewDefiningQuery, schemaName + "." + tableName), sqlConnection)) { DataTable dtQuery = new DataTable("Text"); dataAdapter.Fill(dtQuery); for (int i = 0; i < dtQuery.Rows.Count; i++) { definingQuery += (string)dtQuery.Rows[i]["Text"]; } } return definingQuery; } public override DatabaseObjectsCollection LoadProcedures(IDatabase database) { DatabaseObjectsCollection procedures = new DatabaseObjectsCollection(database); SqlConnection sqlConnection = new SqlConnection(database.ConnectionString); using (SqlDataAdapter da = new SqlDataAdapter(_getProcedures, sqlConnection)) { DataTable dtProcedures = new DataTable("Procedures"); da.Fill(dtProcedures); for (int i = 0; i < dtProcedures.Rows.Count; i++) { Procedure procedure = new Procedure(); procedure.Name = (string)dtProcedures.Rows[i]["ROUTINE_NAME"]; procedure.SchemaName = (string)dtProcedures.Rows[i]["ROUTINE_SCHEMA"]; if (dtProcedures.Rows[i]["DATA_TYPE"] != DBNull.Value) procedure.DataType = (string)dtProcedures.Rows[i]["DATA_TYPE"]; if (dtProcedures.Rows[i]["CHARACTER_MAXIMUM_LENGTH"] != DBNull.Value) procedure.Length = Convert.ToInt32(dtProcedures.Rows[i]["CHARACTER_MAXIMUM_LENGTH"]); if (procedure.Length == -1) { switch (procedure.DataType.ToLower()) { case "varchar": case "nvarchar": procedure.DataType += "(max)"; break; default: break; } } string procedureType = (string)dtProcedures.Rows[i]["ROUTINE_BODY"]; if (procedureType == "SQL") procedure.ProcedureType = ProcedureType.SQL; else procedure.ProcedureType = ProcedureType.External; procedure.Items = new DatabaseObjectsCollection(procedure); DatabaseObjectsCollection procedureParameters = new DatabaseObjectsCollection(procedure); da.SelectCommand = new SqlCommand(string.Format(_getProcedureParameters, procedure.Name, procedure.SchemaName, database.Name), sqlConnection); DataTable dtProcedureParameters = new DataTable("ProcedureParameters"); da.Fill(dtProcedureParameters); for (int j = 0; j < dtProcedureParameters.Rows.Count; j++) { if (string.IsNullOrEmpty((string)dtProcedureParameters.Rows[j]["PARAMETER_NAME"]) && (string)dtProcedureParameters.Rows[j]["IS_RESULT"] == "YES") // = ReturnValue continue; ProcedureParameter procedureParameter = new ProcedureParameter(); procedureParameter.Name = (string)dtProcedureParameters.Rows[j]["PARAMETER_NAME"]; if (procedureParameter.Name.StartsWith("@")) procedureParameter.Name = procedureParameter.Name.Substring(1); if (dtProcedureParameters.Rows[j]["DATA_TYPE"] != DBNull.Value) procedureParameter.DataType = (string)dtProcedureParameters.Rows[j]["DATA_TYPE"]; if (dtProcedureParameters.Rows[j]["CHARACTER_MAXIMUM_LENGTH"] != DBNull.Value) procedureParameter.Length = Convert.ToInt32(dtProcedureParameters.Rows[j]["CHARACTER_MAXIMUM_LENGTH"]); string parameterMode = (string)dtProcedureParameters.Rows[j]["PARAMETER_MODE"]; if (parameterMode == "IN") procedureParameter.ParameterMode = ParameterMode.In; else if (parameterMode == "OUT") procedureParameter.ParameterMode = ParameterMode.Out; else procedureParameter.ParameterMode = ParameterMode.InOut; procedure.Items.Add(procedureParameter); } procedures.Add(procedure); } } return procedures; } public override string ToString() { return this.Name; } public override IDatasource CreateNewIDatasource( string server , string userid , string password ) { SQLServerDatasource ds = new SQLServerDatasource(this); ds.Name = server; ds.Server = server; ds.UserId = userid; ds.Password = password; return ds; } } }