2002-08-23 Nick Drochak <ndrochak@gol.com>
[mono.git] / mcs / class / Mono.Data.MySql / Mono.Data.MySql / MySqlConnection.cs
1 //
2 // Mono.Data.MySql.MyConnection.cs
3 //
4 // Author:
5 //   Daniel Morgan (danmorg@sc.rr.com)
6 //
7 // (C) Daniel Morgan 2002
8 //
9
10 using System;
11 using System.Collections;
12 using System.ComponentModel;
13 using System.Data;
14 using System.Data.Common;
15 using System.Runtime.InteropServices;
16 using System.Text;
17
18 namespace Mono.Data.MySql {
19
20         public sealed class MySqlConnection : Component, IDbConnection,
21                 ICloneable       {
22
23                 #region Fields
24
25                 private IntPtr mysqlInitStruct = IntPtr.Zero;
26                 private IntPtr mysqlConn = IntPtr.Zero;    
27                 
28                 private string connectionString = "";    
29                 private string mysqlConnectionString = ""; 
30                 
31                 //private MySqlTransaction trans = null;
32                 private int connectionTimeout = 15;     
33                 // default for 15 seconds
34
35                 // MySQL connection string parameters
36                 string host = "";
37                 string user = "";
38                 string passwd = "";
39                 string dbname = "";
40                 uint port = MySql.Port;
41                 string socketName = "";
42                 uint flags = 0;
43
44                 // connection state
45                 private ConnectionState conState = ConnectionState.Closed;
46                 
47                 // DataReader state
48                 //private MySqlDataReader rdr = null;
49                 private bool dataReaderOpen = false;
50                 // FIXME: if true, throw an exception if SqlConnection 
51                 //        is used for anything other than reading
52                 //        data using SqlDataReader
53                 
54                 private string versionString = "Unknown";
55                 private bool disposed = false;
56
57                 #endregion // Fields
58
59                 #region Constructors
60
61                 // A lot of the defaults were initialized in the Fields
62                 [MonoTODO]
63                 public MySqlConnection () {
64
65                 }
66         
67                 [MonoTODO]
68                 public MySqlConnection (String connectionString) {
69                         SetConnectionString (connectionString);
70                 }
71
72                 #endregion // Constructors
73
74                 #region Destructors
75         
76                 // aka Finalize
77                 // [ClassInterface(ClassInterfaceType.AutoDual)]
78                 [MonoTODO]
79                 ~MySqlConnection() {
80                         // FIXME: this class need 
81                         //        a destructor to release resources
82                         //        Also, take a look at Dispose
83                         Dispose (false);
84                 }
85                 
86                 #endregion // Destructors
87
88                 #region Public Methods
89
90                 IDbTransaction IDbConnection.BeginTransaction () {
91                         // return BeginTransaction ();
92                         return null;
93                 }
94
95                 //public MySqlTransaction BeginTransaction () {
96                 //      return TransactionBegin (); // call private method
97                 //}
98
99                 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel 
100                         il) {
101                         //return BeginTransaction (il);
102                         return null;
103                 }
104
105                 //public MySqlTransaction BeginTransaction (IsolationLevel il) {
106                 //      return TransactionBegin (il); // call private method
107                 //}
108
109                 //[MonoTODO]
110                 //public MySqlTransaction BeginTransaction(string transactionName) {
111                 //      return TransactionBegin (); // call private method
112                 //}
113
114                 //[MonoTODO]
115                 //public MySqlTransaction BeginTransaction(IsolationLevel iso,\r
116                 //      string transactionName) {
117                 //      return TransactionBegin (iso); // call private method
118                 //}
119
120                 [MonoTODO]
121                 public void ChangeDatabase (string databaseName) {
122                         throw new NotImplementedException ();
123                 }
124                 
125                 object ICloneable.Clone() {
126                         throw new NotImplementedException ();
127                 }
128
129                 [MonoTODO]
130                 public void Close () {
131                         if(dataReaderOpen == true) {
132                                 // TODO: what do I do if
133                                 // the user Closes the connection
134                                 // without closing the Reader first?
135
136                         }                       
137                         CloseDataSource ();
138                 }
139
140                 IDbCommand IDbConnection.CreateCommand () {
141                         return CreateCommand ();
142                 }
143
144                 public MySqlCommand CreateCommand () {
145                         MySqlCommand sqlcmd = new MySqlCommand ("", this);
146
147                         return sqlcmd;
148                 }
149
150                 [MonoTODO]
151                 public void Open () {
152                         if(dbname.Equals(""))
153                                 throw new InvalidOperationException(
154                                         "dbname missing");
155                         else if(conState == ConnectionState.Open)
156                                 throw new InvalidOperationException(
157                                         "ConnnectionState is already Open");            
158
159                         // FIXME: check to make sure we have 
160                         //        everything to connect,
161                         //        otherwise, throw an exception
162
163                         mysqlInitStruct = MySql.Init(IntPtr.Zero);
164                         if (mysqlInitStruct == IntPtr.Zero) {
165                                 // TODO: throw exception instead
166                                 Console.WriteLine("MySQL Init failed.");
167                                 return;
168                         }
169
170                         
171                         // *** this is what it should be ***
172                         //mysqlConn = MySql.Connect(mysqlInitStruct, 
173                         //      host.Equals("") ? null : host, 
174                         //      user.Equals("") ? null : user, 
175                         //      passwd.Equals("") ? null : passwd, 
176                         //      dbname.Equals("") ? null : dbname, 
177                         //      port,
178                         //      socketName.Equals("") ? null : socketName,
179                         //      flags);
180                         //      
181                         mysqlConn = MySql.Connect(mysqlInitStruct, 
182                                 host, 
183                                 user, 
184                                 passwd, 
185                                 dbname, 
186                                 port,
187                                 socketName,
188                                 flags);
189                         if (mysqlConn == IntPtr.Zero) {
190                                 // TODO: throw exception instead
191                                 Console.WriteLine("MySQL Connect failed, "+MySql.Error(mysqlInitStruct));
192                                 return;
193                         }
194
195                         Console.WriteLine("MySql Selecting Database: " + dbname + "...");
196                         Console.Out.Flush();
197                         int sdb = MySql.SelectDb(mysqlInitStruct, dbname);
198                         if (sdb != 0) {
199                                 Console.WriteLine("Error: Can not select the "+dbname+" database.");
200                                 Console.Out.Flush();
201                                 Console.WriteLine("MySql Error: " + MySql.Error(mysqlInitStruct));
202                                 Console.Out.Flush();
203                                 return;
204                         }
205                 
206                         // Successfully Connected
207                         SetupConnection();
208                 }
209
210                 #endregion // Public Methods
211
212                 #region Protected Methods
213                 
214                 [MonoTODO]
215                 
216                 protected override void Dispose(bool disposing) {
217                         if(!this.disposed)
218                                 try {
219                                         if(disposing) {
220                                                 // release any managed resources
221                                         }
222                                         // release any unmanaged resources
223                                         // close any handles
224                                                                                 
225                                         this.disposed = true;
226                                 }
227                                 finally {
228                                         base.Dispose(disposing);
229                                 }
230                 }
231
232                 #endregion
233
234                 #region Internal Methods
235
236                 // Used to prevent MySqlConnection
237                 // from doing anything while
238                 // MySqlDataReader is open.
239                 // Open the Reader. (called from MySqlCommand)
240                 /*
241                 internal void OpenReader(MySqlDataReader reader) {      
242                         if(dataReaderOpen == true) {
243                                 // TODO: throw exception here?
244                                 //       because a reader
245                                 //       is already open
246                         }
247                         else {
248                                 rdr = reader;
249                                 dataReaderOpen = true;
250                         }
251                 }
252                 */
253
254                 // Used to prevent MySqlConnection
255                 // from doing anything while
256                 // MySqlDataReader is open
257                 // Close the Reader (called from MySqlCommand)
258                 // if closeConnection true, Close() the connection
259                 // this is based on CommandBehavior.CloseConnection
260                 internal void CloseReader(bool closeConnection) {
261                         if(closeConnection == true)
262                                 CloseDataSource();
263                         else
264                                 dataReaderOpen = false;
265                 }
266
267                 #endregion // Internal Methods
268
269                 #region Private Methods
270
271                 private void SetupConnection() {
272                         conState = ConnectionState.Open;
273
274                         versionString = GetDatabaseServerVersion();
275
276                         // May need to set DateTime format to ISO
277                         // "YYYY-MM-DD hh:mi:ss.ms"
278                 }
279
280                 private string GetDatabaseServerVersion() {
281                         //MySqlCommand cmd = new MySqlCommand("select version()",this);
282                         //return (string) cmd.ExecuteScalar();
283                         return "";
284                 }
285
286                 private void CloseDataSource () {
287                         // FIXME: just a quick hack
288                         if(conState == ConnectionState.Open) {
289                                 /*
290                                 if(trans != null)
291                                         if(trans.DoingTransaction == true) {
292                                                 trans.Rollback();
293                                                 // trans.Dispose();
294                                                 trans = null;
295                                         }
296                                 */
297                                 conState = ConnectionState.Closed;
298                                 MySql.Close(mysqlInitStruct);
299                                 // MySql.ThreadEnd();
300                                 mysqlConn = IntPtr.Zero;
301                         }
302                 }
303
304                 private void SetConnectionString (string connectionString) {
305
306                         this.connectionString = connectionString;
307                         mysqlConnectionString = ConvertStringToMySql (
308                                 connectionString);
309                 }
310
311                 private String ConvertStringToMySql (String 
312                         oleDbConnectionString) {
313                         StringBuilder mysqlConnection = 
314                                 new StringBuilder();
315                         string result;
316                         string[] connectionParameters;
317
318                         char[] semicolon = new Char[1];
319                         semicolon[0] = ';';
320                         
321                         // FIXME: what is the max number of value pairs 
322                         //        can there be for the OLE DB 
323                         //        connnection string? what about libgda max?
324                         //        what about postgres max?
325
326                         // FIXME: currently assuming value pairs are like:
327                         //        "key1=value1;key2=value2;key3=value3"
328                         //        Need to deal with values that have
329                         //        single or double quotes.  And error 
330                         //        handling of that too.
331                         //        "key1=value1;key2='value2';key=\"value3\""
332
333                         // FIXME: put the connection parameters 
334                         //        from the connection
335                         //        string into a 
336                         //        Hashtable (System.Collections)
337                         //        instead of using private variables 
338                         //        to store them
339                         connectionParameters = oleDbConnectionString.
340                                 Split (semicolon);
341                         foreach (string sParameter in connectionParameters) {
342                                 if(sParameter.Length > 0) {
343                                         BreakConnectionParameter (sParameter);
344                                         mysqlConnection.
345                                                 Append (sParameter + 
346                                                 " ");
347                                 }
348                         }
349                         result = mysqlConnection.ToString ();
350                         return result;
351                 }
352
353                 private bool BreakConnectionParameter (String sParameter) {     
354                         bool addParm = true;
355                         int index;
356
357                         index = sParameter.IndexOf ("=");
358                         if (index > 0) {        
359                                 string parmKey, parmValue;
360
361                                 // separate string "key=value" to 
362                                 // string "key" and "value"
363                                 parmKey = sParameter.Substring (0, index);
364                                 parmValue = sParameter.Substring (index + 1,
365                                         sParameter.Length - index - 1);
366
367                                 /*
368                                  * string host
369                                  * string user
370                                  * string passwd
371                                  * string dbname
372                                  * unit port
373                                  * string socketName
374                                  * unit flags (can be multiple)
375                                  */ 
376                                 switch(parmKey.ToLower()) {
377                                 case "port":
378                                         port = UInt32.Parse(parmValue);
379                                         break;
380
381                                 case "host":
382                                         // set DataSource property
383                                         host = parmValue;
384                                         break;
385
386                                 case "dbname":
387                                         // set Database property
388                                         dbname = parmValue;
389                                         break;
390
391                                 case "user":
392                                         user = parmValue;
393                                         break;
394
395                                 case "passwd":
396                                         passwd = parmValue;
397                                         break;
398
399                                 case "socketName":
400                                         socketName = parmValue;
401                                         break;
402
403                                 default:
404                                         // throw an exception?
405                                         break;
406                                 }
407                         }
408                         return addParm;
409                 }
410
411                 /*
412                 private MySqlTransaction TransactionBegin () {
413                         // FIXME: need to keep track of 
414                         // transaction in-progress
415                         trans = new SqlTransaction ();
416                         // using internal methods of SqlTransaction
417                         trans.SetConnection (this);
418                         trans.Begin();
419
420                         return trans;
421                 }
422
423                 private MySqlTransaction TransactionBegin (IsolationLevel il) {
424                         // FIXME: need to keep track of 
425                         // transaction in-progress
426                         TransactionBegin();
427                         trans.SetIsolationLevel (il);
428                         
429                         return trans;
430                 }
431                 */
432
433                 #endregion
434
435                 #region Public Properties
436
437                 [MonoTODO]
438                 public ConnectionState State            {
439                         get { 
440                                 return conState;
441                         }
442                 }
443
444                 public string ConnectionString  {
445                         get { 
446                                 return connectionString;
447                         }
448                         set { 
449                                 SetConnectionString (value);
450                         }
451                 }
452                 
453                 public int ConnectionTimeout {
454                         get { 
455                                 return connectionTimeout; 
456                         }
457                 }
458
459                 public string Database  {
460                         get { 
461                                 return dbname; 
462                         }
463                 }
464
465                 public string DataSource {
466                         get {
467                                 return host;
468                         }
469                 }
470
471                 public int PacketSize {
472                         get { 
473                                 throw new NotImplementedException ();
474                         }
475                 }
476
477                 public string ServerVersion {
478                         get { 
479                                 return versionString;
480                         }
481                 }
482
483                 #endregion // Public Properties
484
485                 #region Internal Properties
486 /*
487                 // For Mono.Data.MySql classes
488                 // to get the current transaction
489                 // in progress - if any
490                 internal MySqlTransaction Transaction {
491                         get {
492                                 return trans;
493                         }
494                 }
495 */
496                 // For Mono.Data.MySql classes 
497                 // to get the unmanaged MySql connection
498                 internal IntPtr NativeMySqlConnection {
499                         get {
500                                 return mysqlConn;
501                         }
502                 }
503
504                 // For Mono.Data.MySql classes 
505                 // to get the unmanaged MySql connection
506                 internal IntPtr NativeMySqlInitStruct {
507                         get {
508                                 return mysqlInitStruct;
509                         }
510                 }
511
512                 // Used to prevent SqlConnection
513                 // from doing anything while
514                 // SqlDataReader is open
515                 internal bool IsReaderOpen {
516                         get {
517                                 return dataReaderOpen;
518                         }
519                 }
520
521                 #endregion // Internal Properties
522
523                 #region Events
524 /*                
525                 public event 
526                         MyInfoMessageEventHandler InfoMessage;
527
528                 public event 
529                         StateChangeEventHandler StateChange;
530 */              
531                 #endregion
532
533         }
534 }