**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.Data.MySql / Mono.Data.MySql / MySqlConnection.cs
blobad863bba4864c7579be52771d57c23b7a701331e
1 //
2 // Mono.Data.MySql.MyConnection.cs
3 //
4 // Author:
5 // Daniel Morgan (danmorg@sc.rr.com)
6 // Tim Coleman (tim@timcoleman.com)
7 //
8 // (C) Daniel Morgan 2002
9 // Copyright (C) Tim Coleman, 2002
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.ComponentModel;
37 using System.Data;
38 using System.Data.Common;
39 using System.Runtime.InteropServices;
40 using System.Text;
42 namespace Mono.Data.MySql {
44 public sealed class MySqlConnection : Component, ICloneable, IDbConnection {
46 #region Fields
48 private IntPtr mysqlInitStruct = IntPtr.Zero;
49 private IntPtr mysqlConn = IntPtr.Zero;
51 private string connectionString = "";
52 private string mysqlConnectionString = "";
54 private MySqlTransaction trans = null;
55 private int connectionTimeout = 15;
56 // default for 15 seconds
58 // MySQL connection string parameters
59 string host = "";
60 string user = "";
61 string passwd = "";
62 string dbname = "";
63 uint port = MySql.Port;
64 string socketName = "";
65 uint flags = 0;
67 // connection state
68 private ConnectionState conState = ConnectionState.Closed;
70 // DataReader state
71 //private MySqlDataReader rdr = null;
72 private bool dataReaderOpen = false;
73 // FIXME: if true, throw an exception if SqlConnection
74 // is used for anything other than reading
75 // data using SqlDataReader
77 private string versionString = "Unknown";
78 private bool disposed = false;
80 #endregion // Fields
82 #region Constructors
84 // A lot of the defaults were initialized in the Fields
85 [MonoTODO]
86 public MySqlConnection () {
90 [MonoTODO]
91 public MySqlConnection (String connectionString) {
92 SetConnectionString (connectionString);
95 #endregion // Constructors
97 #region Destructors
99 protected override void Dispose(bool disposing) {
100 if(!this.disposed)
101 try {
102 if(disposing) {
103 // release any managed resources
104 trans = null;
106 // release any unmanaged resources
107 mysqlInitStruct = IntPtr.Zero;
108 IntPtr mysqlConn = IntPtr.Zero;
110 // close any handles
112 this.disposed = true;
114 finally {
115 base.Dispose(disposing);
119 // aka Finalize
120 ~MySqlConnection() {
121 Dispose (false);
124 #endregion // Destructors
126 #region Public Methods
128 IDbTransaction IDbConnection.BeginTransaction () {
129 return BeginTransaction ();
132 public MySqlTransaction BeginTransaction () {
133 return TransactionBegin (); // call private method
136 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel
137 il) {
138 return BeginTransaction (il);
141 public MySqlTransaction BeginTransaction (IsolationLevel il) {
142 return TransactionBegin (il); // call private method
145 [MonoTODO]
146 public MySqlTransaction BeginTransaction(string transactionName) {
148 // FIXME: Can MySQL handle named transactions?
149 return TransactionBegin (); // call private method
152 [MonoTODO]
153 public MySqlTransaction BeginTransaction(IsolationLevel iso,
154 string transactionName) {
156 // FIXME: Can MySQL handle named transactions?
157 return TransactionBegin (iso); // call private method
160 public void ChangeDatabase (string databaseName) {
161 dbname = databaseName;
162 int sdb = MySql.SelectDb(mysqlInitStruct, dbname);
163 if (sdb != 0) {
164 string msg =
165 "MySql Error: " +
166 "Can not select the " +
167 dbname +
168 " database because: " +
169 MySql.Error(mysqlInitStruct);
170 throw new MySqlException (msg);
174 object ICloneable.Clone() {
175 throw new NotImplementedException ();
178 [MonoTODO]
179 public void Close () {
180 if(dataReaderOpen == true) {
181 // TODO: what do I do if
182 // the user Closes the connection
183 // without closing the Reader first?
186 CloseDataSource ();
189 IDbCommand IDbConnection.CreateCommand () {
190 return CreateCommand ();
193 public MySqlCommand CreateCommand () {
194 MySqlCommand sqlcmd = new MySqlCommand ("", this);
196 return sqlcmd;
199 [MonoTODO]
200 public void Open () {
201 if(dbname.Equals(""))
202 throw new InvalidOperationException(
203 "dbname missing");
204 else if(conState == ConnectionState.Open)
205 throw new InvalidOperationException(
206 "ConnnectionState is already Open");
207 else if(connectionString.Equals(String.Empty))
208 throw new InvalidOperationException(
209 "ConnectionString is not set");
211 // FIXME: check to make sure we have
212 // everything to connect,
213 // otherwise, throw an exception
215 mysqlInitStruct = MySql.Init(IntPtr.Zero);
216 if (mysqlInitStruct == IntPtr.Zero) {
217 throw new MySqlException("MySQL Init failed.");
221 // *** this is what it should be ***
222 //mysqlConn = MySql.Connect(mysqlInitStruct,
223 // host.Equals("") ? null : host,
224 // user.Equals("") ? null : user,
225 // passwd.Equals("") ? null : passwd,
226 // dbname.Equals("") ? null : dbname,
227 // port,
228 // socketName.Equals("") ? null : socketName,
229 // flags);
231 mysqlConn = MySql.Connect(mysqlInitStruct,
232 host,
233 user,
234 passwd,
235 dbname,
236 port,
237 socketName,
238 flags);
239 if (mysqlConn == IntPtr.Zero) {
240 string msg = "MySQL Connect failed, " +
241 MySql.Error(mysqlInitStruct);
242 throw new MySqlException(msg);
245 this.ChangeDatabase (dbname);
247 // Successfully Connected
248 SetupConnection();
251 #endregion // Public Methods
253 #region Protected Methods
255 #endregion
257 #region Internal Methods
259 // Used to prevent MySqlConnection
260 // from doing anything while
261 // MySqlDataReader is open.
262 // Open the Reader. (called from MySqlCommand)
264 internal void OpenReader(MySqlDataReader reader) {
265 if(dataReaderOpen == true) {
266 // TODO: throw exception here?
267 // because a reader
268 // is already open
270 else {
271 rdr = reader;
272 dataReaderOpen = true;
277 // Used to prevent MySqlConnection
278 // from doing anything while
279 // MySqlDataReader is open
280 // Close the Reader (called from MySqlCommand)
281 // if closeConnection true, Close() the connection
282 // this is based on CommandBehavior.CloseConnection
283 internal void CloseReader(bool closeConnection) {
284 if(closeConnection == true)
285 CloseDataSource();
286 else
287 dataReaderOpen = false;
290 #endregion // Internal Methods
292 #region Private Methods
294 private void SetupConnection() {
295 conState = ConnectionState.Open;
297 versionString = GetDatabaseServerVersion();
300 private string GetDatabaseServerVersion() {
301 MySqlCommand cmd = new MySqlCommand("select version()",this);
302 return (string) cmd.ExecuteScalar();
305 private void CloseDataSource () {
306 // FIXME: just a quick hack
307 if(conState == ConnectionState.Open) {
309 if(trans != null)
310 if(trans.DoingTransaction == true) {
311 trans.Rollback();
312 // trans.Dispose();
313 trans = null;
316 conState = ConnectionState.Closed;
317 MySql.Close(mysqlInitStruct);
318 MySql.ThreadEnd();
319 mysqlConn = IntPtr.Zero;
323 void SetConnectionString (string connectionString) {
324 this.connectionString = connectionString;
326 connectionString += ";";
327 NameValueCollection parameters = new NameValueCollection ();
329 if (connectionString == String.Empty)
330 return;
332 bool inQuote = false;
333 bool inDQuote = false;
335 string name = String.Empty;
336 string value = String.Empty;
337 StringBuilder sb = new StringBuilder ();
339 foreach (char c in connectionString) {
340 switch (c) {
341 case '\'':
342 inQuote = !inQuote;
343 break;
344 case '"' :
345 inDQuote = !inDQuote;
346 break;
347 case ';' :
348 if (!inDQuote && !inQuote) {
349 if (name != String.Empty && name != null) {
350 value = sb.ToString ();
351 parameters [name.ToUpper ().Trim ()] = value.Trim ();
353 name = String.Empty;
354 value = String.Empty;
355 sb = new StringBuilder ();
357 else
358 sb.Append (c);
359 break;
360 case '=' :
361 if (!inDQuote && !inQuote) {
362 name = sb.ToString ();
363 sb = new StringBuilder ();
365 else
366 sb.Append (c);
367 break;
368 default:
369 sb.Append (c);
370 break;
374 SetProperties (parameters);
377 private void SetProperties (NameValueCollection parameters) {
379 StringBuilder connectionStr = new StringBuilder();
381 string value;
382 foreach (string name in parameters) {
383 value = parameters[name];
385 bool found = true;
386 switch (name) {
387 case "PORT" :
388 port = UInt32.Parse(value);
389 break;
390 case "DATA SOURCE" :
391 case "SERVER" :
392 case "HOST" :
393 // set DataSource property
394 host = value;
395 break;
396 case "INITIAL CATALOG" :
397 case "DATABASE" :
398 case "DBNAME" :
399 // set Database property
400 dbname = value;
401 break;
402 case "PASSWORD" :
403 case "PWD" :
404 case "PASSWD" :
405 passwd = value;
406 break;
407 case "USER ID" :
408 case "UID" :
409 case "USER" :
410 user = value;
411 break;
412 case "SOCKETNAME":
413 socketName = value;
414 break;
415 case "FLAGS" :
416 // FIXME: how to get these flags and
417 // and pass to MySQL?
418 // flags is a bitfield
419 flags = UInt32.Parse(value);
420 break;
421 default:
422 found = false;
423 // FIXME: throw exception?
424 break;
426 if (found == true) {
427 string valuePair = name + "=" + value;
428 connectionStr.Append (valuePair + " ");
431 this.mysqlConnectionString = connectionStr.ToString ();
434 private MySqlTransaction TransactionBegin () {
435 // FIXME: need to keep track of
436 // transaction in-progress
437 trans = new MySqlTransaction ();
438 // using internal methods of SqlTransaction
439 trans.SetConnection (this);
440 trans.Begin();
442 return trans;
445 private MySqlTransaction TransactionBegin (IsolationLevel il) {
446 // FIXME: need to keep track of
447 // transaction in-progress
448 trans = new MySqlTransaction ();
449 // using internal methods of MySqlTransaction
450 trans.SetConnection (this);
451 trans.SetIsolationLevel (il);
452 trans.Begin();
454 return trans;
457 #endregion
459 #region Public Properties
461 [MonoTODO]
462 public ConnectionState State {
463 get {
464 return conState;
468 public string ConnectionString {
469 get {
470 return connectionString;
472 set {
473 SetConnectionString (value);
477 public int ConnectionTimeout {
478 get {
479 return connectionTimeout;
483 public string Database {
484 get {
485 return dbname;
489 public string DataSource {
490 get {
491 return host;
495 public int PacketSize {
496 get {
497 throw new NotImplementedException ();
501 public string ServerVersion {
502 get {
503 return versionString;
507 #endregion // Public Properties
509 #region Internal Properties
511 // For Mono.Data.MySql classes
512 // to get the current transaction
513 // in progress - if any
514 internal MySqlTransaction Transaction {
515 get {
516 return trans;
520 // For Mono.Data.MySql classes
521 // to get the unmanaged MySql connection
522 internal IntPtr NativeMySqlConnection {
523 get {
524 return mysqlConn;
528 // For Mono.Data.MySql classes
529 // to get the unmanaged MySql connection
530 internal IntPtr NativeMySqlInitStruct {
531 get {
532 return mysqlInitStruct;
536 // Used to prevent SqlConnection
537 // from doing anything while
538 // SqlDataReader is open
539 internal bool IsReaderOpen {
540 get {
541 return dataReaderOpen;
545 #endregion // Internal Properties
547 #region Events
549 public event
550 MyInfoMessageEventHandler InfoMessage;
552 public event
553 StateChangeEventHandler StateChange;
555 #endregion