[System.Net] Add support for .pac proxy config scripts on mac
[mono.git] / mcs / class / System.Data / Mainsoft.Data.Jdbc.Providers.jvm / OleDbSqlServerProvider.cs
1 //
2 // System.Data.OleDb.OleDbConnection
3 //
4 // Authors:
5 //      Konstantin Triger <kostat@mainsoft.com>
6 //      Boris Kirzner <borisk@mainsoft.com>
7 //      
8 // (C) 2006 Mainsoft Corporation (http://www.mainsoft.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.Data.Common;
35 using System.Data.Configuration;
36 using System.Data.ProviderBase;
37 using Mainsoft.Data.Configuration;
38
39 using java.net;
40 using System.Globalization;
41
42 namespace Mainsoft.Data.Jdbc.Providers
43 {
44         #region OleDbSqlServerProvider2000
45
46         public class OleDbSqlServerProvider2000 : GenericProvider
47         {
48                 #region Consts
49
50                 private const string Port = "Port";
51                 private const string DefaultInstanceName = "MSSQLSERVER";
52                 private const int DefaultTimeout = 15;
53
54                 #endregion //Consts
55
56                 #region Fields
57
58                 #endregion // Fields
59
60                 #region Constructors
61
62                 public OleDbSqlServerProvider2000 (IDictionary providerInfo) : base (providerInfo)
63                 {
64                 }
65
66                 #endregion // Constructors
67
68                 #region Properties
69
70                 #endregion // Properties
71
72                 #region Methods
73
74                 public override IConnectionStringDictionary GetConnectionStringBuilder (string connectionString)
75                 {
76                         //TBD: should wrap the IConnectionStringDictionary
77                         IConnectionStringDictionary conectionStringBuilder = base.GetConnectionStringBuilder (connectionString);
78                         OleDbSqlHelper.InitConnectionStringBuilder (conectionStringBuilder);
79                         
80                         string port = (string) conectionStringBuilder [Port];
81                         if (port == null || port.Length == 0) {
82                                 port = GetMSSqlPort (OleDbSqlHelper.GetInstanceName (conectionStringBuilder, DefaultInstanceName), OleDbSqlHelper.GetDataSource (conectionStringBuilder), OleDbSqlHelper.GetTimeout (conectionStringBuilder, DefaultTimeout));
83                                 conectionStringBuilder.Add (Port, port);
84                         }
85
86                         return conectionStringBuilder;
87                 }
88
89                 static string GetMSSqlPort(string instanceName, string dataSource, int timeout) {
90                         string port = String.Empty;
91                         try {
92                                 DatagramSocket socket = new DatagramSocket();
93
94                                 // send request
95                                 sbyte[] buf = new sbyte[] {2};
96                                 InetAddress address = InetAddress.getByName(dataSource);
97                                 DatagramPacket packet = new DatagramPacket(buf, buf.Length, address, 1434);
98                                 socket.send(packet);
99                                 sbyte[] recbuf = new sbyte[1024];
100                                 packet = new DatagramPacket(recbuf, recbuf.Length, packet.getAddress(), packet.getPort());
101
102                                 // try to receive from socket while increasing timeouts in geometric progression
103                                 int iterationTimeout = 1;
104                                 int totalTimeout = 0;
105                                 for(;;) {
106                                         socket.setSoTimeout(iterationTimeout);
107                                         try {
108                                                 socket.receive(packet);
109                                                 break;
110                                         }
111                                         catch (SocketTimeoutException e) {
112                                                 totalTimeout += iterationTimeout;
113                                                 iterationTimeout *= 2;
114                                                 if (totalTimeout >= timeout*1000) {
115                                                         throw new java.sql.SQLException(
116                                                                 String.Format ("Unable to retrieve the port number for {0} using UDP on port 1434. Please see your network administrator to solve this problem or add the port number of your SQL server instance to your connection string (i.e. port=1433).", dataSource)
117                                                                 );
118                                                 }
119                                         }
120                                 }
121                                 sbyte[] rcvdSbytes = packet.getData();
122                                 char[] rcvdChars = new char[rcvdSbytes.Length];
123                                 for(int i=0; i < rcvdSbytes.Length; i++) {
124                                         rcvdChars[i] = (char)rcvdSbytes[i];
125                                 }
126                                 String received = new String(rcvdChars);
127
128                                 java.util.StringTokenizer st = new java.util.StringTokenizer(received, ";");
129                                 String prev = "";
130                                 bool instanceReached = instanceName == null || instanceName.Length == 0;
131                                 while (st.hasMoreTokens()) {
132                                         if (!instanceReached) {
133                                                 if (prev.Trim().Equals("InstanceName")) {
134                                                         if (String.Compare(instanceName,st.nextToken().Trim(),true, CultureInfo.InvariantCulture) == 0) {
135                                                                 instanceReached = true;
136                                                         }
137                                                 }
138                                         }
139                                         else {
140                                                 if (prev.Trim().Equals("tcp")) {
141                                                         port = st.nextToken().Trim();
142                                                         //ensure we got a valid int
143                                                         java.lang.Integer.parseInt(port);
144                                                         break;
145                                                 }
146                                         }
147                                         prev = st.nextToken();
148                                 }
149                                 socket.close();
150
151                                 if (!instanceReached)
152                                         throw new java.sql.SQLException(
153                                                 String.Format ("Specified SQL Server '{0}\\{1}' not found.", dataSource, instanceName)
154                                                 );
155                                 return port;
156
157                         }
158                         catch (java.sql.SQLException) {
159                                 throw;
160                         }
161                         catch (Exception e) {
162                                 throw new java.sql.SQLException(e.Message);
163                         }
164                 }
165
166                 #endregion // Methods
167         }
168
169         #endregion // OleDbSqlServerProvider2000
170
171         #region OleDbSqlServerProvider2005
172
173         public class OleDbSqlServerProvider2005 : GenericProvider
174         {
175                 #region Consts
176
177                 #endregion //Consts
178
179                 #region Fields
180
181                 #endregion // Fields
182
183                 #region Constructors
184
185                 public OleDbSqlServerProvider2005 (IDictionary providerInfo) : base (providerInfo)
186                 {
187                 }
188
189                 #endregion // Constructors
190
191                 #region Properties
192
193                 #endregion // Properties
194
195                 #region Methods
196
197                 public override IConnectionStringDictionary GetConnectionStringBuilder (string connectionString)
198                 {
199                         //TBD: should wrap the IConnectionStringDictionary
200                         IConnectionStringDictionary conectionStringBuilder = base.GetConnectionStringBuilder (connectionString);
201                         OleDbSqlHelper.InitConnectionStringBuilder (conectionStringBuilder);
202                         return conectionStringBuilder;
203                 }               
204                 
205                 public override java.sql.Connection GetConnection(IConnectionStringDictionary conectionStringBuilder)
206                 {
207                         return new SqlServer2005Connection (base.GetConnection (conectionStringBuilder));
208                 }
209
210                 #endregion // Methods
211
212                 #region SqlServer2005Connection
213
214                 sealed class SqlServer2005Connection : Connection
215                 {
216                         #region Constructors
217
218                         public SqlServer2005Connection(java.sql.Connection connection) : base (connection)
219                         {
220                         }
221
222                         #endregion
223
224                         #region Methods
225
226                         public override java.sql.DatabaseMetaData getMetaData()
227                         {
228                                 return new SqlServer2005DatabaseMetaData (base.getMetaData ());
229                         }
230
231                         #endregion
232                 }
233
234                 #endregion
235
236                 #region SqlServer2005DatabaseMetaData
237
238                 sealed class SqlServer2005DatabaseMetaData : DatabaseMetaData
239                 {
240                         #region Fields
241
242                         #endregion // Fields
243
244                         #region Constructors
245
246                         public SqlServer2005DatabaseMetaData (java.sql.DatabaseMetaData databaseMetaData) : base (databaseMetaData)
247                         {
248                         }
249
250                         #endregion // Constructors
251
252                         #region Properties
253
254                         #endregion // Properties
255
256                         #region Methods
257
258                         public override java.sql.ResultSet getProcedureColumns(string arg_0, string arg_1, string arg_2, string arg_3)
259                         {
260                                 return new SqlServer2005DatbaseMetaDataResultSet (Wrapped.getProcedureColumns (arg_0, arg_1, arg_2, arg_3));
261                         }
262
263                         #endregion // Methods                                           
264                 }
265
266                 #endregion
267
268                 #region SqlServer2005DatbaseMetaDataResultSet
269
270                 sealed class SqlServer2005DatbaseMetaDataResultSet : ResultSet
271                 {
272                         #region Consts
273
274                         private const string DataType = "DATA_TYPE";
275
276                         #endregion
277
278                         #region Fields
279
280                         #endregion // Fields
281
282                         #region Constructors
283
284                         public SqlServer2005DatbaseMetaDataResultSet (java.sql.ResultSet resultSet) : base (resultSet)
285                         {
286                         }
287
288                         #endregion // Constructors
289
290                         #region Properties
291
292                         #endregion // Properties
293
294                         #region Methods
295
296                         public override int getInt(int arg_0)
297                         {
298                                 int res = base.getInt (arg_0);
299                                 if (res == -9) // sql server 2005 jdbc driver value for NVARCHAR
300                                         if (String.CompareOrdinal (getMetaData ().getColumnName (arg_0), DataType) == 0)
301                                                 return java.sql.Types.VARCHAR;
302                                 if (res == -8) // sql server 2005 jdbc driver value for NVARCHAR
303                                         if (String.CompareOrdinal (getMetaData ().getColumnName (arg_0), DataType) == 0)
304                                                 return java.sql.Types.CHAR;
305                                 return res;
306                         }
307
308                         public override int getInt(string arg_0)
309                         {
310                                 int res = base.getInt (arg_0);
311
312                                 if (res == -9) // sql server 2005 jdbc driver value for NVARCHAR
313                                         if (String.CompareOrdinal (arg_0, DataType) == 0)
314                                                 return java.sql.Types.VARCHAR;
315
316                                 if (res == -8) // sql server 2005 jdbc driver value for NVARCHAR
317                                         if (String.CompareOrdinal (arg_0, DataType) == 0)
318                                                 return java.sql.Types.CHAR;
319                                 return res;
320                         }
321
322                         #endregion // Methods   
323                 }
324
325                 #endregion
326         }
327
328         #endregion // OleDbSqlServerProvider2005
329
330         #region OleDbSqlHelper
331
332         class OleDbSqlHelper
333         {
334                 private const string Database = "Database";
335                 private const string ServerName = "ServerName";
336                 private const string Timeout = "Timeout";
337
338                 internal static void InitConnectionStringBuilder (IConnectionStringDictionary conectionStringBuilder)
339                 {
340                         if (!conectionStringBuilder.Contains("jndi-datasource-name")) {
341
342                                 string database = (string) conectionStringBuilder [Database];
343                                 if (database == null)
344                                         conectionStringBuilder.Add (Database, String.Empty);
345
346                                 string dataSource = GetDataSource (conectionStringBuilder);
347                                 string instanceName = GetInstanceName (conectionStringBuilder, null);
348
349                                 if (instanceName != null)
350                                         conectionStringBuilder [ServerName] = dataSource + "\\" + instanceName;
351                                 else
352                                         conectionStringBuilder [ServerName] = dataSource;                                               
353                         }
354                 }               
355
356                 // TBD : refactor GetInstanceName and GetDataSource to single method
357                 internal static string GetInstanceName (IDictionary keyMapper, string defaultInstanceName)
358                 {
359                         string dataSource = (string) keyMapper [ServerName];
360                         string instanceName = String.Empty;
361                         int instanceIdx;
362                         if (dataSource == null || (instanceIdx = dataSource.IndexOf ("\\")) == -1) 
363                                 // no named instance specified - use a default name
364                                 return defaultInstanceName;
365                         else 
366                                 // get named instance name
367                                 return dataSource.Substring (instanceIdx + 1);
368                 }
369
370                 internal static string GetDataSource (IDictionary keyMapper)
371                 {
372                         string dataSource = (string) keyMapper [ServerName];
373                         int instanceIdx;
374                         if (dataSource != null && (instanceIdx = dataSource.IndexOf ("\\")) != -1)
375                                 // throw out named instance name
376                                 dataSource = dataSource.Substring (0,instanceIdx);
377
378                         if (dataSource != null && dataSource.StartsWith ("(") && dataSource.EndsWith (")"))                                     
379                                 dataSource = dataSource.Substring (1,dataSource.Length - 2);
380
381                         if (String.Empty.Equals (dataSource) || (String.Compare ("local", dataSource, true, CultureInfo.InvariantCulture) == 0) || (String.CompareOrdinal (".", dataSource) == 0)) 
382                                 dataSource = "localhost";
383
384                         return dataSource;
385                 }
386
387                 internal static int GetTimeout (IDictionary keyMapper, int defaultTimeout)
388                 {
389                         string timeoutStr = (string) keyMapper [Timeout];
390                         if ((timeoutStr != null) && (timeoutStr.Length != 0)) {
391                                 try {
392                                         return Convert.ToInt32(timeoutStr);
393                                 }
394                                 catch(FormatException e) {
395                                         throw ExceptionHelper.InvalidValueForKey("connect timeout");
396                                 }
397                                 catch (OverflowException e) {
398                                         throw ExceptionHelper.InvalidValueForKey("connect timeout");
399                                 }
400                         }
401                         return defaultTimeout;
402                 }
403         }
404
405         #endregion // OleDbSqlHelper
406
407 }