[System.Data] Fixes tests build with mobile profiles
[mono.git] / mcs / class / System.Data / Test / ProviderTests / Common / ConnectionManager.cs
1 // ConnectionManager.cs - Singleton ConnectionManager class to manage
2 // database connections for test cases.
3 //
4 // Authors:
5 //      Sureshkumar T (tsureshkumar@novell.com)
6 // 
7 // Copyright Novell Inc., and the individuals listed on the
8 // ChangeLog entries.
9 //
10 //
11 // Permission is hereby granted, free of charge, to any person
12 // obtaining a copy of this software and associated documentation
13 // files (the "Software"), to deal in the Software without
14 // restriction, including without limitation the rights to use, copy,
15 // modify, merge, publish, distribute, sublicense, and/or sell copies
16 // of the Software, and to permit persons to whom the Software is
17 // furnished to do so, subject to the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 // SOFTWARE.
30
31 using System;
32 using System.Collections.Generic;
33 using System.Configuration;
34 using System.Data;
35 using System.Data.Common;
36 #if !NO_ODBC
37 using System.Data.Odbc;
38 #endif
39 using System.Data.SqlClient;
40 using System.IO;
41 using System.Linq;
42 using System.Text.RegularExpressions;
43 using NUnit.Framework;
44
45 namespace MonoTests.System.Data.Connected
46 {
47         public class ConnectionManager
48         {
49                 private static ConnectionManager instance;
50                 private ConnectionHolder<SqlConnection> sql;
51
52                 private const string OdbcEnvVar = "SYSTEM_DATA_ODBC";
53                 private const string SqlEnvVar = "SYSTEM_DATA_MSSQL";
54
55                 private ConnectionManager ()
56                 {
57                         //Environment.SetEnvironmentVariable(OdbcEnvVar, @"mysql-odbc|Driver={MySQL ODBC 5.3 Unicode Driver};server=127.0.0.1;uid=sa;pwd=qwerty123;");
58                         //Environment.SetEnvironmentVariable(SqlEnvVar, @"sqlserver-tds|server=127.0.0.1;database=master;user id=sa;password=qwerty123");
59
60                         // Generate a random db name
61                         DatabaseName = "monotest" + Guid.NewGuid().ToString().Substring(0, 7);
62
63                         sql = ConnectionHolder<SqlConnection>.FromEnvVar(SqlEnvVar);
64                         if (sql != null)
65                                 CreateMssqlDatabase();
66                         
67 #if !NO_ODBC
68                         odbc = ConnectionHolder<OdbcConnection>.FromEnvVar(OdbcEnvVar);
69                         if (odbc != null)
70                                 CreateMysqlDatabase();
71 #endif
72                 }
73
74                 private void CreateMssqlDatabase()
75                 {
76                         DBHelper.ExecuteNonQuery(sql.Connection, $"CREATE DATABASE [{DatabaseName}]");
77                         sql.Connection.ChangeDatabase(DatabaseName);
78
79                         string query = File.ReadAllText(@"Test/ProviderTests/sql/sqlserver.sql");
80
81                         var queries = SplitSqlStatements(query);
82                         foreach (var subQuery in queries)
83                         {
84                                 DBHelper.ExecuteNonQuery(sql.Connection, subQuery);
85                         }
86                 }
87
88 #if !NO_ODBC
89                 private void CreateMysqlDatabase()
90                 {
91                         DBHelper.ExecuteNonQuery(odbc.Connection, $"CREATE DATABASE {DatabaseName}");
92                         odbc.Connection.ChangeDatabase(DatabaseName);
93                         odbc.ConnectionString += $"database={DatabaseName}";
94
95                         string query = File.ReadAllText("Test/ProviderTests/sql/MySQL_5.sql");
96
97                         var groups = query.Replace("delimiter ", "")
98                                 .Split(new[] { "//\n" }, StringSplitOptions.RemoveEmptyEntries);
99
100                         foreach (var subQuery in groups[0].Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Concat(groups.Skip(1)))
101                         {
102                                 DBHelper.ExecuteNonQuery(odbc.Connection, subQuery);
103                         }
104                 }
105 #endif
106
107                 private void DropMssqlDatabase()
108                 {
109                         sql.Connection.ChangeDatabase("master");
110                         string query = $"ALTER DATABASE [{DatabaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;\nDROP DATABASE [{DatabaseName}]";
111                         DBHelper.ExecuteNonQuery(sql.Connection, query);
112                 }
113
114 #if !NO_ODBC
115                 private void DropMysqlDatabase()
116                 {
117                         string query = $"DROP DATABASE [{DatabaseName}]";
118                         DBHelper.ExecuteNonQuery(odbc.Connection, query);
119                 }
120 #endif
121
122                 // Split SQL script by "GO" statements
123                 private static IEnumerable<string> SplitSqlStatements(string sqlScript)
124                 {
125                         var statements = Regex.Split(sqlScript,
126                                         $@"^[\t ]*GO[\t ]*\d*[\t ]*(?:--.*)?$",
127                                         RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
128                         return statements.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim(' ', '\r', '\n'));
129                 }
130
131                 public static ConnectionManager Instance => instance ?? (instance = new ConnectionManager());
132
133                 public string DatabaseName { get; }
134
135 #if !NO_ODBC
136
137                 private ConnectionHolder<OdbcConnection> odbc;
138
139                 public ConnectionHolder<OdbcConnection> Odbc
140                 {
141                         get
142                         {
143                                 if (odbc == null)
144                                         Assert.Ignore($"{OdbcEnvVar} environment variable is not set");
145                                 return odbc;
146                         }
147                 }
148 #endif
149
150                 public ConnectionHolder<SqlConnection> Sql
151                 {
152                         get
153                         {
154                                 if (sql == null)
155                                         Assert.Ignore($"{SqlEnvVar} environment variable is not set");
156                                 return sql;
157                         }
158                 }
159
160                 public void Close()
161                 {
162                         sql?.CloseConnection();
163 #if !NO_ODBC                    
164                         odbc?.CloseConnection();
165 #endif
166                 }
167         }
168
169         public class ConnectionHolder<TConnection> where TConnection : DbConnection
170         {
171                 private TConnection connection;
172
173                 public EngineConfig EngineConfig { get; }
174
175                 public TConnection Connection
176                 {
177                         get
178                         {
179                                 if (!(connection.State == ConnectionState.Closed || 
180                                         connection.State == ConnectionState.Broken))
181                                         connection.Close();
182                                 connection.ConnectionString = ConnectionString;
183                                 connection.Open();
184                                 return connection;
185                         }
186                 }
187
188                 public void CloseConnection()
189                 {
190                         if (connection != null && connection.State != ConnectionState.Closed)
191                                 connection.Close();
192                 }
193
194                 public string ConnectionString { get; set; }
195
196                 public ConnectionHolder(EngineConfig engineConfig, DbProviderFactory dbProviderFactory, string connectionString)
197                 {
198                         EngineConfig = engineConfig;
199                         connection = (TConnection)dbProviderFactory.CreateConnection();
200                         ConnectionString = connectionString;
201                 }
202
203                 public static ConnectionHolder<TConnection> FromEnvVar(string envVarName)
204                 {
205 #if NO_CONFIGURATION
206                         throw new NotImplementedException ();
207 #else
208                         string variable = Environment.GetEnvironmentVariable(envVarName) ?? string.Empty;
209                         var envParts = variable.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
210                         if (envParts.Length == 0 || string.IsNullOrEmpty(envParts[0]))
211                                 return null;
212
213                         string connectionName = envParts[0];
214                         string connectionString = variable.Remove(0, envParts[0].Length + 1);
215
216                         ConnectionConfig[] connections = null;
217                         try
218                         {
219                                 connections = (ConnectionConfig[]) ConfigurationManager.GetSection("providerTests");
220                         }
221                         catch
222                         {
223                                 return null;
224                         }
225
226                         foreach (ConnectionConfig connConfig in connections)
227                         {
228                                 if (connConfig.Name != connectionName)
229                                         continue;
230
231                                 DbProviderFactory factory = DbProviderFactories.GetFactory(connConfig.Factory);
232                                 return new ConnectionHolder<TConnection>(connConfig.Engine, factory, connectionString);
233                         }
234                         throw new InvalidOperationException($"Connection {connectionName} not found");
235 #endif
236                 }
237         }
238 }