1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
7
using System.Collections.Generic;
11
using System.Data.Sql;
12
using ICSharpCode.Data.Core.Common;
13
using ICSharpCode.Data.Core.Interfaces;
14
using System.Collections.ObjectModel;
15
using ICSharpCode.Data.Core.DatabaseObjects;
16
using System.Data.SqlClient;
17
using System.Collections.Specialized;
18
using ICSharpCode.Data.Core.Enums;
20
using System.Windows.Threading;
24
namespace ICSharpCode.Data.Core.DatabaseDrivers.SQLServer
26
public class SQLServerDatabaseDriver : DatabaseDriver<SQLServerDatasource>
30
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";
32
private const string _getColumnsScript = @"DECLARE @tablename varchar(100) SET @tablename = N'{0}'
34
clmns.column_id AS [ColumnId],
36
usrt.name AS [DataType],
37
ISNULL(baset.name, N'') AS [SystemType],
38
CAST(CASE WHEN baset.name IN (N'nchar', N'nvarchar') AND clmns.max_length <> -1 THEN
39
clmns.max_length/2 ELSE 0 END AS INT) AS [Length],
40
CAST(clmns.precision AS int) AS [NumericPrecision],
41
clmns.default_object_id AS [DefaultObjectId],
42
clmns.is_ansi_padded AS [IsAnsiPadded],
43
clmns.is_column_set AS [IsColumnSet],
44
clmns.is_computed AS [IsComputed],
45
clmns.is_dts_replicated AS [IsDtsReplicated],
46
clmns.is_filestream AS [IsFileStream],
47
clmns.is_identity AS [IsIdentity],
48
clmns.is_merge_published AS [IsMergePublished],
49
clmns.is_non_sql_subscribed AS [IsNonSqlSubscribed],
50
clmns.is_nullable AS [IsNullable],
51
clmns.is_replicated AS [IsReplicated],
52
clmns.is_rowguidcol AS [IsRowGuidCol],
53
clmns.is_sparse AS [IsSparse],
54
clmns.is_xml_document AS [IsXmlDocument],
55
clmns.object_id AS [ObjectId],
56
clmns.rule_object_id AS [RuleObjectId],
57
clmns.scale AS [Scale],
58
clmns.system_type_id AS [SystemTypeId],
59
clmns.user_type_id AS [UserTypeId],
60
clmns.xml_collection_id AS [XMLCollectionId],
63
SELECT c.name AS ColumnName
64
FROM sys.key_constraints AS k
65
JOIN sys.tables AS t ON t.object_id = k.parent_object_id
66
JOIN sys.schemas AS s ON s.schema_id = t.schema_id
67
JOIN sys.index_columns AS ic ON ic.object_id = t.object_id AND ic.index_id = k.unique_index_id
68
JOIN sys.columns AS c ON c.object_id = t.object_id AND c.column_id = ic.column_id
69
WHERE t.name=@tablename AND c.name = clmns.name)
70
IS NULL THEN 0 ELSE 1 END AS BIT) AS [IsPrimaryKey]
73
INNER JOIN sys.all_columns AS clmns ON clmns.object_id=tbl.object_id
74
LEFT OUTER JOIN sys.types AS usrt ON usrt.user_type_id = clmns.user_type_id
75
LEFT OUTER JOIN sys.types AS baset ON baset.user_type_id = clmns.system_type_id and
76
baset.user_type_id = baset.system_type_id
78
(tbl.name=@tablename and SCHEMA_NAME(tbl.schema_id)=N'{2}')
82
private const string _getConstraintsScript = @"SELECT
83
FKTable = FK.TABLE_NAME,
84
FKColumn = CU.COLUMN_NAME,
85
PKTable = PK.TABLE_NAME,
86
PKColumn = PT.COLUMN_NAME,
87
ConstraintName = C.CONSTRAINT_NAME
89
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
91
INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
92
ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
94
INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
95
ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
97
INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
98
ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
102
i1.TABLE_NAME, i2.COLUMN_NAME
104
INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
106
INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2
107
ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
108
WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY'
110
ON PT.TABLE_NAME = PK.TABLE_NAME
115
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";
116
private const string _getViewDefiningQuery = @"EXEC sp_helptext '{0}'";
117
private const string _getProcedures = "SELECT ROUTINE_NAME, ROUTINE_SCHEMA, ROUTINE_BODY, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE'";
118
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}'";
122
public override string Name
124
get { return "MS SQL Server"; }
127
public override string ProviderName
129
get { return "System.Data.SqlClient"; }
132
public override string ODBCProviderName
134
get { return "SQLNCLI10.1"; }
137
public override void PopulateDatasources()
139
DatabaseObjectsCollection<SQLServerDatasource> datasources = new DatabaseObjectsCollection<SQLServerDatasource>(null);
141
DataTable dt = SqlDataSourceEnumerator.Instance.GetDataSources();
143
foreach (DataRow dr in dt.Rows)
145
string serverName = dr["ServerName"].ToString().Trim().ToUpper();
146
string instanceName = null;
147
string version = null;
149
if (dr["InstanceName"] != null && dr["InstanceName"] != DBNull.Value)
150
instanceName = dr["InstanceName"].ToString().Trim().ToUpper();
152
if (dr["Version"] != null && dr["Version"] != DBNull.Value)
153
version = dr["Version"].ToString().Trim().Split('.').FirstOrDefault();
155
SQLServerDatasource datasource = new SQLServerDatasource(this) { Name = serverName };
158
datasource.ProviderManifestToken = "2000";
159
else if (version == "9")
160
datasource.ProviderManifestToken = "2005";
161
else if (version == "10")
162
datasource.ProviderManifestToken = "2008";
164
if (!String.IsNullOrEmpty(instanceName))
165
datasource.Name += "\\" + instanceName;
167
datasources.Add(datasource);
170
Datasources = datasources;
173
public override void PopulateDatabases(IDatasource datasource)
175
DatabaseObjectsCollection<IDatabase> databases = new DatabaseObjectsCollection<IDatabase>(datasource);
177
SqlConnection sqlConnection = null;
178
sqlConnection = new SqlConnection();
179
sqlConnection.ConnectionString = datasource.ConnectionString;
182
sqlConnection.Open();
184
catch (SqlException ex)
191
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
193
Datasources.Remove(datasource as SQLServerDatasource);
203
string sqlversion = sqlConnection.ServerVersion;
204
sqlversion = sqlversion.Split('.').FirstOrDefault(); //major version
205
int intsqlversion = Convert.ToInt32(sqlversion);
206
string sql = string.Empty;
208
if (intsqlversion == 8)
209
datasource.ProviderManifestToken = "2000";
210
else if (intsqlversion == 9)
211
datasource.ProviderManifestToken = "2005";
212
else if (intsqlversion == 10)
213
datasource.ProviderManifestToken = "2008";
215
if (intsqlversion >= 9)
216
sql = "use master; select name from sys.databases order by name";
218
sql = "use master; select name from sysdatabases order by name";
220
SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection);
221
sqlCommand.CommandTimeout = 20;
223
SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
225
while (sqlDataReader.Read())
227
databases.Add(new Database(datasource) { Name = sqlDataReader["name"].ToString() });
230
sqlDataReader.Close();
232
datasource.Databases = databases;
234
if (sqlConnection != null && sqlConnection.State == ConnectionState.Open)
235
sqlConnection.Close();
238
private void LoadColumns(SqlConnection sqlConnection, ITable table, TableType tableType)
240
string tableTypeName = "tables";
242
if (tableType == TableType.View)
243
tableTypeName = "views";
245
using (SqlDataAdapter dataAdapter =
246
new SqlDataAdapter(string.Format(_getColumnsScript, table.TableName, tableTypeName, table.SchemaName), sqlConnection))
248
DataTable dtColumns = new DataTable("Columns");
249
dataAdapter.Fill(dtColumns);
251
for (int j = 0; j < dtColumns.Rows.Count; j++)
253
Column column = new Column(table);
254
column.ColumnId = (int)dtColumns.Rows[j]["ColumnId"];
255
column.Name = (string)dtColumns.Rows[j]["Name"];
256
column.DataType = (string)dtColumns.Rows[j]["DataType"];
257
column.SystemType = (string)dtColumns.Rows[j]["SystemType"];
258
column.Length = Convert.ToInt32(dtColumns.Rows[j]["Length"]);
260
if (column.Length == -1)
262
switch (column.DataType.ToLower())
266
column.DataType += "(max)";
272
switch (column.SystemType.ToLower())
276
column.SystemType += "(max)";
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"];
289
table.Items.Add(column);
294
public override DatabaseObjectsCollection<ITable> LoadTables(IDatabase database)
296
DatabaseObjectsCollection<ITable> tables = new DatabaseObjectsCollection<ITable>(database);
298
SqlConnection sqlConnection = new SqlConnection(database.ConnectionString);
300
using (SqlDataAdapter da = new SqlDataAdapter(_getConstraintsScript, sqlConnection))
302
DataTable dtConstraints = new DataTable("Constraints");
303
da.Fill(dtConstraints);
305
for (int i = 0; i < dtConstraints.Rows.Count; i++)
307
string constraintName = (string)dtConstraints.Rows[i]["ConstraintName"];
309
IConstraint constraint = database.Constraints.FirstOrDefault(c => c.Name == constraintName);
311
if (constraint == null)
313
constraint = new ICSharpCode.Data.Core.DatabaseObjects.Constraint();
314
constraint.Name = constraintName;
315
constraint.FKTableName = (string)dtConstraints.Rows[i]["FKTable"];
316
constraint.PKTableName = (string)dtConstraints.Rows[i]["PKTable"];
318
database.Constraints.Add(constraint);
321
constraint.FKColumnNames.Add((string)dtConstraints.Rows[i]["FKColumn"]);
322
constraint.PKColumnNames.Add((string)dtConstraints.Rows[i]["PKColumn"]);
326
using (SqlDataAdapter da = new SqlDataAdapter(_getTables, sqlConnection))
328
DataTable dtTables = new DataTable("Tables");
331
for (int i = 0; i < dtTables.Rows.Count; i++)
333
string schemaName = (string)dtTables.Rows[i]["TABLE_SCHEMA"];
334
string tableName = (string)dtTables.Rows[i]["TABLE_NAME"];
336
Table table = new Table() { SchemaName = schemaName, TableName = tableName };
337
LoadColumns(sqlConnection, table, TableType.Table);
339
table.Constraints = database.Constraints.Where(constraint => constraint.FKTableName == tableName).ToDatabaseObjectsCollection(table);
347
public override DatabaseObjectsCollection<IView> LoadViews(IDatabase database)
349
DatabaseObjectsCollection<IView> views = new DatabaseObjectsCollection<IView>(database);
351
SqlConnection sqlConnection = new SqlConnection(database.ConnectionString);
353
using (SqlDataAdapter da = new SqlDataAdapter(_getViews, sqlConnection))
355
DataTable dtViews = new DataTable("Views");
358
for (int i = 0; i < dtViews.Rows.Count; i++)
360
string schemaName = (string)dtViews.Rows[i]["TABLE_SCHEMA"];
361
string viewName = (string)dtViews.Rows[i]["TABLE_NAME"];
363
View view = new View() { SchemaName = schemaName, TableName = viewName, Query = LoadViewQuery(sqlConnection, schemaName, viewName) };
364
LoadColumns(sqlConnection, view, TableType.View);
372
private string LoadViewQuery(SqlConnection sqlConnection, string schemaName, string tableName)
374
string definingQuery = string.Empty;
376
using (SqlDataAdapter dataAdapter =
377
new SqlDataAdapter(string.Format(_getViewDefiningQuery, schemaName + "." + tableName), sqlConnection))
379
DataTable dtQuery = new DataTable("Text");
380
dataAdapter.Fill(dtQuery);
382
for (int i = 0; i < dtQuery.Rows.Count; i++)
384
definingQuery += (string)dtQuery.Rows[i]["Text"];
388
return definingQuery;
391
public override DatabaseObjectsCollection<IProcedure> LoadProcedures(IDatabase database)
393
DatabaseObjectsCollection<IProcedure> procedures = new DatabaseObjectsCollection<IProcedure>(database);
395
SqlConnection sqlConnection = new SqlConnection(database.ConnectionString);
397
using (SqlDataAdapter da = new SqlDataAdapter(_getProcedures, sqlConnection))
399
DataTable dtProcedures = new DataTable("Procedures");
400
da.Fill(dtProcedures);
402
for (int i = 0; i < dtProcedures.Rows.Count; i++)
404
Procedure procedure = new Procedure();
405
procedure.Name = (string)dtProcedures.Rows[i]["ROUTINE_NAME"];
406
procedure.SchemaName = (string)dtProcedures.Rows[i]["ROUTINE_SCHEMA"];
407
if (dtProcedures.Rows[i]["DATA_TYPE"] != DBNull.Value)
408
procedure.DataType = (string)dtProcedures.Rows[i]["DATA_TYPE"];
409
if (dtProcedures.Rows[i]["CHARACTER_MAXIMUM_LENGTH"] != DBNull.Value)
410
procedure.Length = Convert.ToInt32(dtProcedures.Rows[i]["CHARACTER_MAXIMUM_LENGTH"]);
412
if (procedure.Length == -1)
414
switch (procedure.DataType.ToLower())
418
procedure.DataType += "(max)";
425
string procedureType = (string)dtProcedures.Rows[i]["ROUTINE_BODY"];
426
if (procedureType == "SQL")
427
procedure.ProcedureType = ProcedureType.SQL;
429
procedure.ProcedureType = ProcedureType.External;
432
procedure.Items = new DatabaseObjectsCollection<IProcedureParameter>(procedure);
434
DatabaseObjectsCollection<IProcedureParameter> procedureParameters = new DatabaseObjectsCollection<IProcedureParameter>(procedure);
436
da.SelectCommand = new SqlCommand(string.Format(_getProcedureParameters, procedure.Name, procedure.SchemaName, database.Name), sqlConnection);
437
DataTable dtProcedureParameters = new DataTable("ProcedureParameters");
438
da.Fill(dtProcedureParameters);
440
for (int j = 0; j < dtProcedureParameters.Rows.Count; j++)
442
if (string.IsNullOrEmpty((string)dtProcedureParameters.Rows[j]["PARAMETER_NAME"]) &&
443
(string)dtProcedureParameters.Rows[j]["IS_RESULT"] == "YES") // = ReturnValue
446
ProcedureParameter procedureParameter = new ProcedureParameter();
447
procedureParameter.Name = (string)dtProcedureParameters.Rows[j]["PARAMETER_NAME"];
448
if (procedureParameter.Name.StartsWith("@"))
449
procedureParameter.Name = procedureParameter.Name.Substring(1);
451
if (dtProcedureParameters.Rows[j]["DATA_TYPE"] != DBNull.Value)
452
procedureParameter.DataType = (string)dtProcedureParameters.Rows[j]["DATA_TYPE"];
454
if (dtProcedureParameters.Rows[j]["CHARACTER_MAXIMUM_LENGTH"] != DBNull.Value)
455
procedureParameter.Length = Convert.ToInt32(dtProcedureParameters.Rows[j]["CHARACTER_MAXIMUM_LENGTH"]);
457
string parameterMode = (string)dtProcedureParameters.Rows[j]["PARAMETER_MODE"];
458
if (parameterMode == "IN")
459
procedureParameter.ParameterMode = ParameterMode.In;
460
else if (parameterMode == "OUT")
461
procedureParameter.ParameterMode = ParameterMode.Out;
463
procedureParameter.ParameterMode = ParameterMode.InOut;
465
procedure.Items.Add(procedureParameter);
468
procedures.Add(procedure);