MySql - fixed problem where socket was not getting closed properly (thanks Steve!)
[mono.git] / mcs / class / ByteFX.Data / mysqlclient / Connection.cs
1 // ByteFX.Data data access components for .Net
2 // Copyright (C) 2002-2003  ByteFX, Inc.
3 //
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 // 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // Lesser General Public License for more details.
13 // 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18 using System;
19 using System.Data;
20 using System.Collections.Specialized;
21 using System.Text;
22 using System.ComponentModel;
23 using System.Globalization;
24 using ByteFX.Data.Common;
25
26 namespace ByteFX.Data.MySqlClient
27 {
28         /// <summary>
29         /// Represents an open connection to a MySQL Server database. This class cannot be inherited.
30         /// </summary>
31         /// <include file='docs/MySqlConnection.xml' path='MyDocs/MyMembers[@name="Class"]/*'/>
32         [System.Drawing.ToolboxBitmap( typeof(MySqlConnection), "Designers.connection.bmp")]
33         [System.ComponentModel.DesignerCategory("Code")]
34         [ToolboxItem(true)]
35         public sealed class MySqlConnection : Component, IDbConnection, ICloneable
36         {
37                 internal ConnectionState                        state;
38                 private  MySqlInternalConnection        internalConnection;
39                 private  MySqlDataReader                        dataReader;
40                 private  NumberFormatInfo               numberFormat;
41                 private  MySqlConnectionString  settings;
42
43                 public event StateChangeEventHandler    StateChange;
44
45
46                 /// <summary>
47                 /// Creates a new connection
48                 /// </summary>
49                 public MySqlConnection()
50                 {
51                         settings = new MySqlConnectionString();
52                 }
53
54                 /// <summary>
55                 /// Creates a new connection
56                 /// </summary>
57                 /// <param name="container"></param>
58                 public MySqlConnection(System.ComponentModel.IContainer container)
59                 {
60                         settings = new MySqlConnectionString();
61                 }
62     
63
64                 // Have a constructor that takes a connection string.
65                 /// <summary>
66                 /// Creates a new connection using the specified connection string.
67                 /// </summary>
68                 /// <param name="connectString"></param>
69                 public MySqlConnection(string connectString)
70                 {
71                         settings = new MySqlConnectionString(connectString);
72                 }
73
74                 /// <summary>
75                 /// Gets the name of the MySQL server to which to connect.
76                 /// </summary>
77                 #region Properties
78                 [Browsable(true)]
79                 public string DataSource
80                 {
81                         get { return settings.Host; }
82                 }
83
84                 /// <summary>
85                 /// Gets the time to wait while trying to establish a connection before terminating the attempt and generating an error.
86                 /// </summary>
87                 /// <include file='docs/MySqlConnection.xml' path='MyDocs/MyMembers[@name="ConnectionTimeout"]/*'/>
88                 [Browsable(true)]
89                 public int ConnectionTimeout
90                 {
91                         get { return settings.ConnectTimeout; }
92                 }
93                 
94                 /// <summary>
95                 /// Gets the name of the current database or the database to be used after a connection is opened.
96                 /// </summary>
97                 [Browsable(true)]
98                 public string Database
99                 {
100                         get     { return settings.Database; }
101                 }
102
103                 /// <summary>
104                 /// Indicates if this connection should use compression when communicating with the server.
105                 /// </summary>
106                 [Browsable(false)]
107                 public bool UseCompression
108                 {
109                         get { return settings.UseCompression; }
110                 }
111                 
112                 /// <summary>
113                 /// Gets the current state of the connection.
114                 /// </summary>
115                 [Browsable(false)]
116                 public ConnectionState State
117                 {
118                         get { return state; }
119                 }
120
121                 internal MySqlDataReader Reader
122                 {
123                         get { return dataReader; }
124                         set { dataReader = value; }
125                 }
126
127                 internal MySqlInternalConnection InternalConnection
128                 {
129                         get { return internalConnection; }
130                 }
131
132                 internal NumberFormatInfo NumberFormat
133                 {
134                         get 
135                         {
136                                 if (numberFormat == null)
137                                 {
138                                         numberFormat = new NumberFormatInfo();
139                                         numberFormat = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone();
140                                         numberFormat.NumberDecimalSeparator = ".";
141                                 }
142                                 return numberFormat;
143                         }
144                 }
145
146                 /// <summary>
147                 /// Gets a string containing the version of the MySQL server to which the client is connected.
148                 /// </summary>
149                 [Browsable(false)]
150                 public string ServerVersion 
151                 {
152                         get { return ""; } //internalConnection.GetServerVersion(); }
153                 }
154
155                 internal Encoding Encoding 
156                 {
157                         get 
158                         {
159 //TODO                          if (encoding == null)
160                                         return System.Text.Encoding.Default;
161 //                              else 
162 //                                      return encoding;
163                         }
164                 }
165
166
167                 /// <summary>
168                 /// Gets or sets the string used to connect to a MySQL Server database.
169                 /// </summary>
170 #if WINDOWS
171                 [Editor(typeof(Designers.ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
172 #endif
173                 [Browsable(true)]
174                 [Category("Data")]
175                 public string ConnectionString
176                 {
177                         get
178                         {
179                                 // Always return exactly what the user set.
180                                 // Security-sensitive information may be removed.
181                                 return settings.ConnectString;
182                         }
183                         set
184                         {
185                                 settings.ConnectString = value;
186                                 if (internalConnection != null)
187                                         internalConnection.Settings = settings;
188                         }
189                 }
190
191                 #endregion
192
193                 #region Transactions
194                 /// <summary>
195                 /// Begins a database transaction.
196                 /// </summary>
197                 /// <returns></returns>
198                 public MySqlTransaction BeginTransaction()
199                 {
200                         if (state != ConnectionState.Open)
201                                 throw new MySqlException("Invalid operation: The connection is closed");
202
203                         MySqlTransaction t = new MySqlTransaction();
204                         t.Connection = this;
205                         InternalConnection.Driver.SendCommand( DBCmd.QUERY, "BEGIN");
206                         return t;
207                 }
208
209                 /// <summary>
210                 /// 
211                 /// </summary>
212                 IDbTransaction IDbConnection.BeginTransaction()
213                 {
214                         return BeginTransaction();
215                 }
216
217                 /// <summary>
218                 /// 
219                 /// </summary>
220                 /// <param name="level"></param>
221                 /// <returns></returns>
222                 public MySqlTransaction BeginTransaction(IsolationLevel level)
223                 {
224                         if (state != ConnectionState.Open)
225                                 throw new MySqlException("Invalid operation: The connection is closed");
226
227                         MySqlTransaction t = new MySqlTransaction();
228                         t.Connection = this;
229                         t.IsolationLevel = level;
230                         string cmd = "SET SESSION TRANSACTION ISOLATION LEVEL ";
231                         switch (level) 
232                         {
233                                 case IsolationLevel.ReadCommitted:
234                                         cmd += "READ COMMITTED"; break;
235                                 case IsolationLevel.ReadUncommitted:
236                                         cmd += "READ UNCOMMITTED"; break;
237                                 case IsolationLevel.RepeatableRead:
238                                         cmd += "REPEATABLE READ"; break;
239                                 case IsolationLevel.Serializable:
240                                         cmd += "SERIALIZABLE"; break;
241                                 case IsolationLevel.Chaos:
242                                         throw new NotSupportedException("Chaos isolation level is not supported");
243                         }
244                         InternalConnection.Driver.SendCommand( DBCmd.QUERY, cmd );
245                         InternalConnection.Driver.SendCommand( DBCmd.QUERY, "BEGIN");
246                         return t;
247                 }
248
249                 /// <summary>
250                 /// 
251                 /// </summary>
252                 /// <param name="level"></param>
253                 /// <returns></returns>
254                 IDbTransaction IDbConnection.BeginTransaction(IsolationLevel level)
255                 {
256                         return BeginTransaction(level);
257                 }
258                 #endregion
259
260                 /// <summary>
261                 /// Changes the current database for an open MySqlConnection.
262                 /// </summary>
263                 /// <param name="dbName"></param>
264                 public void ChangeDatabase(string dbName)
265                 {
266                         if (state != ConnectionState.Open)
267                                 throw new MySqlException("Invalid operation: The connection is closed");
268
269                         //TODOinternalConnection.ChangeDatabase( dbName );
270                         InternalConnection.Driver.SendCommand( DBCmd.INIT_DB, dbName );
271                 }
272
273                 internal void SetState( ConnectionState newState ) 
274                 {
275                         ConnectionState oldState = state;
276                         state = newState;
277                         if (this.StateChange != null)
278                                 StateChange(this, new StateChangeEventArgs( oldState, newState ));
279                 }
280
281                 /// <summary>
282                 /// Opens a database connection with the property settings specified by the ConnectionString.
283                 /// </summary>
284                 public void Open()
285                 {
286                         if (state == ConnectionState.Open)
287                                 throw new MySqlException("error connecting: The connection is already Open (state=Open).");
288
289                         SetState( ConnectionState.Connecting );
290
291                         if (settings.Pooling) 
292                         {
293                                 internalConnection = MySqlPoolManager.GetConnection( settings );
294                         }
295                         else
296                         {
297                                 internalConnection = new MySqlInternalConnection( settings );
298                                 internalConnection.Open();
299                         }
300
301                         SetState( ConnectionState.Open );
302                         internalConnection.SetServerVariables(this);
303                         ChangeDatabase( settings.Database );
304                 }
305
306
307                 /// <summary>
308                 /// Closes the connection to the database. This is the preferred method of closing any open connection.
309                 /// </summary>
310                 public void Close()
311                 {
312                         if (state == ConnectionState.Closed) return;
313
314                         if (dataReader != null)
315                                 dataReader.Close();
316
317                         if (settings.Pooling)
318                                 MySqlPoolManager.ReleaseConnection( internalConnection );
319                         else
320                                 internalConnection.Close();
321
322                         SetState( ConnectionState.Closed );
323                 }
324
325                 IDbCommand IDbConnection.CreateCommand()
326                 {
327                         return CreateCommand();
328                 }
329
330                 /// <summary>
331                 /// Creates and returns a MySqlCommand object associated with the MySqlConnection.
332                 /// </summary>
333                 /// <returns></returns>
334                 public MySqlCommand CreateCommand()
335                 {
336                         // Return a new instance of a command object.
337                         MySqlCommand c = new MySqlCommand();
338                         c.Connection = this;
339                         return c;
340                 }
341
342                 #region ICloneable
343                 public object Clone()
344                 {
345                         MySqlConnection clone = new MySqlConnection();
346                         clone.ConnectionString = this.ConnectionString;
347                         //TODO:  how deep should this go?
348                         return clone;
349                 }
350                 #endregion
351
352                 #region IDisposeable
353                 /// <summary>
354                 /// Releases the resources used by the MySqlConnection.
355                 /// </summary>
356                 public new void Dispose() 
357                 {
358                         if (State == ConnectionState.Open)
359                                 Close();
360                         base.Dispose();
361                 }
362                 #endregion
363   }
364 }