Merge pull request #3686 from lambdageek/dev-format-printf
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlConnection.cs
1 //
2 // System.Data.SqlClient.SqlConnection.cs
3 //
4 // Authors:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //   Phillip Jerkins (Phillip.Jerkins@morgankeegan.com)
9 //   Diego Caravana (diego@toth.it)
10 //
11 // Copyright (C) Ximian, Inc 2002
12 // Copyright (C) Daniel Morgan 2002, 2003
13 // Copyright (C) Tim Coleman, 2002, 2003
14 // Copyright (C) Phillip Jerkins, 2003
15 //
16
17 //
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
19 //
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 // 
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 // 
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 //
39
40 using Mono.Data.Tds;
41 using Mono.Data.Tds.Protocol;
42 using System;
43 using System.Collections;
44 using System.Collections.Specialized;
45 using System.ComponentModel;
46 using System.Data;
47 using System.Data.Common;
48 #if !MOBILE
49 using System.EnterpriseServices;
50 #endif
51 using System.Globalization;
52 using System.Net;
53 using System.Net.Sockets;
54 using System.Text;
55 using System.Xml;
56 using System.Collections.Generic;
57 using System.Security;
58
59 namespace System.Data.SqlClient
60 {
61         [DefaultEvent ("InfoMessage")]
62         public sealed class SqlConnection : DbConnection, IDbConnection, ICloneable
63         {
64                 #region Fields
65
66                 bool disposed;
67
68                 // The set of SQL connection pools
69                 static TdsConnectionPoolManager sqlConnectionPools = new TdsConnectionPoolManager (TdsVersion.tds80);
70                 const int DEFAULT_PACKETSIZE = 8000;
71                 const int MAX_PACKETSIZE = 32768;
72                 const int MIN_PACKETSIZE = 512;
73                 const int DEFAULT_CONNECTIONTIMEOUT = 15;
74                 const int DEFAULT_CONNECTIONLIFETIME = 0;
75                 const int DEFAULT_MAXPOOLSIZE = 100;
76                 const int MIN_MAXPOOLSIZE = 1;
77                 const int DEFAULT_MINPOOLSIZE = 0;
78                 const int DEFAULT_PORT = 1433;
79
80                 // The current connection pool
81                 TdsConnectionPool pool;
82
83                 // The connection string that identifies this connection
84                 string connectionString;
85
86                 // The connection credentials
87                 SqlCredential credentials;
88
89                 // The transaction object for the current transaction
90                 SqlTransaction transaction;
91
92                 // Connection parameters
93                 
94                 TdsConnectionParameters parms;
95                 bool connectionReset;
96                 int connectionLifeTime;
97                 bool pooling;
98                 string dataSource;
99                 int connectionTimeout;
100                 int minPoolSize;
101                 int maxPoolSize;
102                 int packetSize;
103                 int port;
104                 bool fireInfoMessageEventOnUserErrors;
105                 bool statisticsEnabled;
106                 
107                 // The current state
108                 ConnectionState state = ConnectionState.Closed;
109
110                 SqlDataReader dataReader;
111                 XmlReader xmlReader;
112
113                 // The TDS object
114                 Tds tds;
115
116                 #endregion // Fields
117
118                 #region Constructors
119
120                 public SqlConnection () : this (null)
121                 {
122                 }
123         
124                 public SqlConnection (string connectionString)
125                 {
126                         ConnectionString = connectionString;
127                 }
128
129                 public SqlConnection (string connectionString, SqlCredential cred)
130                 {
131                         ConnectionString = connectionString;
132                         Credentials = cred;
133                 }
134
135                 #endregion // Constructors
136
137                 #region Properties
138
139                 [DefaultValue ("")]
140                 [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
141 #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
142                 [RecommendedAsConfigurable(true)]
143 #pragma warning restore 618
144                 [RefreshProperties (RefreshProperties.All)]
145                 public override string ConnectionString {
146                         get {
147                                 if (connectionString == null)
148                                         return string.Empty;
149                                 return connectionString;
150                         }
151                         [MonoTODO("persist security info, encrypt, enlist keyword not implemented")]
152                         set {
153                                 if (state == ConnectionState.Open)
154                                         throw new InvalidOperationException ("Not Allowed to change ConnectionString property while Connection state is OPEN");
155                                 SetConnectionString (value);
156                         }
157                 }
158         
159                 public SqlCredential Credentials {
160                         get {
161                                 return credentials;
162                         }
163                         set {
164                                 credentials = value;
165                         }
166                 }
167         
168                 [MonoTODO]
169                 public Guid ClientConnectionId {
170                         get {
171                                 throw new NotImplementedException ();
172                         }
173                 }
174
175                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
176                 public
177                 override
178                 int ConnectionTimeout {
179                         get { return connectionTimeout; }
180                 }
181
182                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
183                 public
184                 override
185                 string Database {
186                         get {
187                                 if (State == ConnectionState.Open)
188                                         return tds.Database; 
189                                 return parms.Database ;
190                         }
191                 }
192                 
193                 internal SqlDataReader DataReader {
194                         get { return dataReader; }
195                         set { dataReader = value; }
196                 }
197
198                 [Browsable(true)]
199                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
200                 public
201                 override
202                 string DataSource {
203                         get { return dataSource; }
204                 }
205
206                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
207                 public int PacketSize {
208                         get {
209                                 if (State == ConnectionState.Open)
210                                         return ((Tds) tds).PacketSize;
211                                 return packetSize;
212                         }
213                 }
214
215                 [Browsable (false)]
216                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
217                 public
218                 override
219                 string ServerVersion {
220                         get {
221                                 if (state == ConnectionState.Closed)
222                                         throw ExceptionHelper.ConnectionClosed ();
223                                 else
224                                         return tds.ServerVersion; 
225                         }
226                 }
227
228                 [Browsable (false)]
229                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
230                 public
231                 override
232                 ConnectionState State {
233                         get { return state; }
234                 }
235
236                 internal Tds Tds {
237                         get { return tds; }
238                 }
239
240                 internal SqlTransaction Transaction {
241                         get { return transaction; }
242                         set { transaction = value; }
243                 }
244
245                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
246                 public string WorkstationId {
247                         get { return parms.Hostname; }
248                 }
249
250                 internal XmlReader XmlReader {
251                         get { return xmlReader; }
252                         set { xmlReader = value; }
253                 }
254
255                 public bool FireInfoMessageEventOnUserErrors { 
256                         get { return fireInfoMessageEventOnUserErrors; } 
257                         set { fireInfoMessageEventOnUserErrors = value; }
258                 }
259                 
260                 [DefaultValue (false)]
261                 public bool StatisticsEnabled { 
262                         get { return statisticsEnabled; } 
263                         set { statisticsEnabled = value; }
264                 }
265
266                 protected override DbProviderFactory DbProviderFactory {
267                         get {
268                                 return SqlClientFactory.Instance;
269                         }
270                 }
271
272                 #endregion // Properties
273
274                 #region Events
275
276                 public event SqlInfoMessageEventHandler InfoMessage;
277
278
279                 #endregion // Events
280
281                 #region Delegates
282
283                 private void ErrorHandler (object sender, TdsInternalErrorMessageEventArgs e)
284                 {
285                         try {
286                                 if (!tds.IsConnected)
287                                         Close ();
288                         } catch {
289                                 try {
290                                         Close ();
291                                 } catch {
292                                 }
293                         }
294                         throw new SqlException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SqlClient Data Provider", e.State);
295                 }
296
297                 private void MessageHandler (object sender, TdsInternalInfoMessageEventArgs e)
298                 {
299                         OnSqlInfoMessage (CreateSqlInfoMessageEvent (e.Errors));
300                 }
301
302                 #endregion // Delegates
303
304                 #region Methods
305
306                 public new SqlTransaction BeginTransaction ()
307                 {
308                         return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
309                 }
310
311                 public new SqlTransaction BeginTransaction (IsolationLevel iso)
312                 {
313                         return BeginTransaction (iso, String.Empty);
314                 }
315
316                 public SqlTransaction BeginTransaction (string transactionName)
317                 {
318                         return BeginTransaction (IsolationLevel.ReadCommitted, transactionName);
319                 }
320
321                 public SqlTransaction BeginTransaction (IsolationLevel iso, string transactionName)
322                 {
323                         if (state == ConnectionState.Closed)
324                                 throw ExceptionHelper.ConnectionClosed ();
325                         if (transaction != null)
326                                 throw new InvalidOperationException ("SqlConnection does not support parallel transactions.");
327
328                         string isolevel = String.Empty;
329                         switch (iso) {
330                         case IsolationLevel.ReadUncommitted:
331                                 isolevel = "READ UNCOMMITTED";
332                                 break;
333                         case IsolationLevel.RepeatableRead:
334                                 isolevel = "REPEATABLE READ";
335                                 break;
336                         case IsolationLevel.Serializable:
337                                 isolevel = "SERIALIZABLE";
338                                 break;
339                         case IsolationLevel.ReadCommitted:
340                                 isolevel = "READ COMMITTED";
341                                 break;
342                         case IsolationLevel.Snapshot:
343                                 isolevel = "SNAPSHOT";
344                                 break;
345                         case IsolationLevel.Unspecified:
346                                 iso = IsolationLevel.ReadCommitted;
347                                 isolevel = "READ COMMITTED";
348                                 break;
349                         case IsolationLevel.Chaos:
350                                 throw new ArgumentOutOfRangeException ("IsolationLevel",
351                                         string.Format (CultureInfo.CurrentCulture,
352                                                 "The IsolationLevel enumeration " +
353                                                 "value, {0}, is not supported by " +
354                                                 "the .Net Framework SqlClient " +
355                                                 "Data Provider.", (int) iso));
356                         default:
357                                 throw new ArgumentOutOfRangeException ("IsolationLevel",
358                                         string.Format (CultureInfo.CurrentCulture,
359                                                 "The IsolationLevel enumeration value, {0}, is invalid.",
360                                                 (int) iso));
361                         }
362
363                         tds.Execute (String.Format ("SET TRANSACTION ISOLATION LEVEL {0};BEGIN TRANSACTION {1}", isolevel, transactionName));
364                         
365                         transaction = new SqlTransaction (this, iso);
366                         return transaction;
367                 }
368
369                 public
370                 override
371                 void ChangeDatabase (string database)
372                 {
373                         if (!IsValidDatabaseName (database))
374                                 throw new ArgumentException (String.Format ("The database name {0} is not valid.", database));
375                         if (state != ConnectionState.Open)
376                                 throw new InvalidOperationException ("The connection is not open.");
377                         tds.Execute (String.Format ("use [{0}]", database));
378                 }
379
380                 private void ChangeState (ConnectionState currentState)
381                 {
382                         if (currentState == state)
383                                 return;
384
385                         ConnectionState originalState = state;
386                         state = currentState;
387                         OnStateChange (CreateStateChangeEvent (originalState, currentState));
388                 }
389
390                 public
391                 override
392                 void Close ()
393                 {
394                         if (transaction != null && transaction.IsOpen)
395                                 transaction.Rollback ();
396
397                         if (dataReader != null || xmlReader != null) {
398                                 if(tds != null) tds.SkipToEnd ();
399                                 dataReader = null;
400                                 xmlReader = null;
401                         }
402
403                         if (tds != null && tds.IsConnected) {
404                                 if (pooling && tds.Pooling) {
405                                         if (pool != null) {
406                                                 pool.ReleaseConnection (tds);
407                                                 pool = null;
408                                         }
409                                 } else {
410                                         tds.Disconnect ();
411                                 }
412                         }
413
414                         if (tds != null) {
415                                 tds.TdsErrorMessage -= new TdsInternalErrorMessageEventHandler (ErrorHandler);
416                                 tds.TdsInfoMessage -= new TdsInternalInfoMessageEventHandler (MessageHandler);
417                         }
418
419                         ChangeState (ConnectionState.Closed);
420                 }
421
422                 public new SqlCommand CreateCommand ()
423                 {
424                         SqlCommand command = new SqlCommand ();
425                         command.Connection = this;
426                         return command;
427                 }
428                 
429                 private SqlInfoMessageEventArgs CreateSqlInfoMessageEvent (TdsInternalErrorCollection errors)
430                 {
431                         foreach (TdsInternalError e in errors)
432                                 return new SqlInfoMessageEventArgs (new SqlException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SqlClient Data Provider", e.State));
433
434                         return null;
435                 }
436
437                 private StateChangeEventArgs CreateStateChangeEvent (ConnectionState originalState, ConnectionState currentState)
438                 {
439                         return new StateChangeEventArgs (originalState, currentState);
440                 }
441
442                 protected override void Dispose (bool disposing)
443                 {
444                         try {
445                                 if (disposing && !disposed) {
446                                         if (State == ConnectionState.Open) 
447                                                 Close ();
448                                         ConnectionString = null;
449                                 }
450                         } finally {
451                                 disposed = true;
452                                 base.Dispose (disposing);
453                         }
454                 }
455
456 #if !MOBILE
457                 [MonoTODO ("Not sure what this means at present.")]
458                 public void EnlistDistributedTransaction (ITransaction transaction)
459                 {
460                         throw new NotImplementedException ();
461                 }
462 #endif
463
464                 object ICloneable.Clone ()
465                 {
466                         return new SqlConnection (ConnectionString);
467                 }
468
469                 protected override DbTransaction BeginDbTransaction (IsolationLevel isolationLevel)
470                 {
471                         return BeginTransaction (isolationLevel);
472                 }
473
474                 protected override DbCommand CreateDbCommand ()
475                 {
476                         return CreateCommand ();
477                 }
478
479                 public
480                 override
481                 void Open ()
482                 {
483                         string serverName = string.Empty;
484                         if (state == ConnectionState.Open)
485                                 throw new InvalidOperationException ("The Connection is already Open (State=Open)");
486
487                         if (connectionString == null || connectionString.Trim().Length == 0)
488                                 throw new InvalidOperationException ("Connection string has not been initialized.");
489
490                         try {
491                                 if (!pooling) {
492                                         if(!ParseDataSource (dataSource, out port, out serverName))
493                                                 throw new SqlException(20, 0, "SQL Server does not exist or access denied.",  17, "ConnectionOpen (Connect()).", dataSource, parms.ApplicationName, 0);
494                                         tds = new Tds80 (serverName, port, PacketSize, ConnectionTimeout, 0);
495                                         tds.Pooling = false;
496                                 }
497                                 else {
498                                         if(!ParseDataSource (dataSource, out port, out serverName))
499                                                 throw new SqlException(20, 0, "SQL Server does not exist or access denied.",  17, "ConnectionOpen (Connect()).", dataSource, parms.ApplicationName, 0);
500                                         
501                                         TdsConnectionInfo info = new TdsConnectionInfo (serverName, port, packetSize, ConnectionTimeout, minPoolSize, maxPoolSize, connectionLifeTime);
502                                         pool = sqlConnectionPools.GetConnectionPool (connectionString, info);
503                                         tds = pool.GetConnection ();
504                                 }
505                         } catch (TdsTimeoutException e) {
506                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
507                         } catch (TdsInternalException e) {
508                                 throw SqlException.FromTdsInternalException (e);
509                         }
510
511                         tds.TdsErrorMessage += new TdsInternalErrorMessageEventHandler (ErrorHandler);
512                         tds.TdsInfoMessage += new TdsInternalInfoMessageEventHandler (MessageHandler);
513
514                         if (!tds.IsConnected) {
515                                 try {
516                                         if (Credentials != null) {
517                                                 if (parms.User != String.Empty)
518                                                         throw new ArgumentException("UserID already specified");
519                                                 if (parms.PasswordSet)
520                                                         throw new ArgumentException("Password already specified");
521                                                 if (parms.DomainLogin != false)
522                                                         throw new ArgumentException("Cannot use credentials with DomainLogin");
523                                                 parms.User = Credentials.UserId;
524                                                 parms.Password = Credentials.Password;
525                                         }
526                                         tds.Connect (parms);
527                                 } catch {
528                                         if (pooling)
529                                                 pool.ReleaseConnection (tds);
530                                         throw;
531                                 }
532                         }
533
534                         disposed = false; // reset this, so using () would call Close ().
535                         ChangeState (ConnectionState.Open);
536                 }
537
538                 private bool ParseDataSource (string theDataSource, out int thePort, out string theServerName)
539                 {
540                         theServerName = string.Empty;
541                         string theInstanceName = string.Empty;
542         
543                         if (theDataSource == null)
544                                 throw new ArgumentException("Format of initialization string does not conform to specifications");
545
546                         thePort = DEFAULT_PORT; // default TCP port for SQL Server
547                         bool success = true;
548
549                         int idx = 0;
550                         if ((idx = theDataSource.IndexOf (',')) > -1) {
551                                 theServerName = theDataSource.Substring (0, idx);
552                                 string p = theDataSource.Substring (idx + 1);
553                                 thePort = Int32.Parse (p);
554                         } else if ((idx = theDataSource.IndexOf ('\\')) > -1) {
555                                 theServerName = theDataSource.Substring (0, idx);
556                                 theInstanceName = theDataSource.Substring (idx + 1);
557
558                                 // do port discovery via UDP port 1434
559                                 port = DiscoverTcpPortViaSqlMonitor (theServerName, theInstanceName);
560                                 if (port == -1)
561                                         success = false;
562                         } else
563                                 theServerName = theDataSource;
564
565                         if (theServerName.Length == 0 || theServerName == "(local)" || theServerName == ".")
566                                 theServerName = "localhost";
567
568                         if ((idx = theServerName.IndexOf ("tcp:")) > -1)
569                                 theServerName = theServerName.Substring (idx + 4);
570
571                         return success;
572                 }
573
574                 private bool ConvertIntegratedSecurity (string value)
575                 {
576                         if (value.ToUpper() == "SSPI")
577                                 return true;
578
579                         return ConvertToBoolean ("integrated security", value, false);
580                 }
581
582                 private bool ConvertToBoolean (string key, string value, bool defaultValue)
583                 {
584                         if (value.Length == 0)
585                                 return defaultValue;
586
587                         string upperValue = value.ToUpper ();
588
589                         if (upperValue == "TRUE" || upperValue == "YES")
590                                 return true;
591                         else if (upperValue == "FALSE" || upperValue == "NO")
592                                 return false;
593
594                         throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
595                                 "Invalid value \"{0}\" for key '{1}'.", value, key));
596                 }
597
598                 private int ConvertToInt32 (string key, string value, int defaultValue)
599                 {
600                         if (value.Length == 0)
601                                 return defaultValue;
602
603                         try {
604                                 return int.Parse (value);
605                         } catch (Exception ex) {
606                                 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
607                                         "Invalid value \"{0}\" for key '{1}'.", value, key), ex);
608                         }
609                 }
610
611                 private int DiscoverTcpPortViaSqlMonitor (string ServerName, string InstanceName) 
612                 {
613                         SqlMonitorSocket msock;
614                         msock = new SqlMonitorSocket (ServerName, InstanceName);
615                         int SqlServerPort = msock.DiscoverTcpPort (ConnectionTimeout);
616                         msock = null;
617                         return SqlServerPort;
618                 }
619         
620                 void SetConnectionString (string connectionString)
621                 {
622                         SetDefaultConnectionParameters ();
623
624                         if ((connectionString == null) || (connectionString.Trim().Length == 0)) {
625                                 this.connectionString = connectionString;
626                                 return;
627                         }
628
629                         connectionString += ";";
630
631                         bool inQuote = false;
632                         bool inDQuote = false;
633                         bool inName = true;
634
635                         string name = String.Empty;
636                         string value = String.Empty;
637                         StringBuilder sb = new StringBuilder ();
638
639                         for (int i = 0; i < connectionString.Length; i += 1) {
640                                 char c = connectionString [i];
641                                 char peek;
642                                 if (i == connectionString.Length - 1)
643                                         peek = '\0';
644                                 else
645                                         peek = connectionString [i + 1];
646
647                                 switch (c) {
648                                 case '\'':
649                                         if (inDQuote)
650                                                 sb.Append (c);
651                                         else if (peek.Equals (c)) {
652                                                 sb.Append (c);
653                                                 i += 1;
654                                         }
655                                         else
656                                                 inQuote = !inQuote;
657                                         break;
658                                 case '"':
659                                         if (inQuote)
660                                                 sb.Append (c);
661                                         else if (peek.Equals (c)) {
662                                                 sb.Append (c);
663                                                 i += 1;
664                                         }
665                                         else
666                                                 inDQuote = !inDQuote;
667                                         break;
668                                 case ';':
669                                         if (inDQuote || inQuote)
670                                                 sb.Append (c);
671                                         else {
672                                                 if (name != String.Empty && name != null) {
673                                                         value = sb.ToString ();
674                                                         SetProperties (name.ToLower ().Trim() , value);
675                                                 }
676                                                 else if (sb.Length != 0)
677                                                         throw new ArgumentException ("Format of initialization string does not conform to specifications");
678                                                 inName = true;
679                                                 name = String.Empty;
680                                                 value = String.Empty;
681                                                 sb = new StringBuilder ();
682                                         }
683                                         break;
684                                 case '=':
685                                         if (inDQuote || inQuote || !inName)
686                                                 sb.Append (c);
687                                         else if (peek.Equals (c)) {
688                                                 sb.Append (c);
689                                                 i += 1;
690
691                                         }
692                                         else {
693                                                 name = sb.ToString ();
694                                                 sb = new StringBuilder ();
695                                                 inName = false;
696                                         }
697                                         break;
698                                 case ' ':
699                                         if (inQuote || inDQuote)
700                                                 sb.Append (c);
701                                         else if (sb.Length > 0 && !peek.Equals (';'))
702                                                 sb.Append (c);
703                                         break;
704                                 default:
705                                         sb.Append (c);
706                                         break;
707                                 }
708                         }
709
710                         if (minPoolSize > maxPoolSize)
711                                 throw new ArgumentException ("Invalid value for "
712                                         + "'min pool size' or 'max pool size'; "
713                                         + "'min pool size' must not be greater "
714                                         + "than 'max pool size'.");
715
716                         connectionString = connectionString.Substring (0 , connectionString.Length-1);
717                         this.connectionString = connectionString;
718                 }
719
720                 void SetDefaultConnectionParameters ()
721                 {
722                         if (parms == null)
723                                 parms = new TdsConnectionParameters ();
724                         else
725                                 parms.Reset ();
726                         dataSource = string.Empty;
727                         connectionTimeout = DEFAULT_CONNECTIONTIMEOUT;
728                         connectionLifeTime = DEFAULT_CONNECTIONLIFETIME;
729                         connectionReset = true;
730                         pooling = true;
731                         maxPoolSize = DEFAULT_MAXPOOLSIZE;
732                         minPoolSize = DEFAULT_MINPOOLSIZE;
733                         packetSize = DEFAULT_PACKETSIZE;
734                         port = DEFAULT_PORT;
735                         async = false;
736                 }
737
738                 private void SetProperties (string name , string value)
739                 {
740                         switch (name) {
741                         case "app" :
742                         case "application name" :
743                                 parms.ApplicationName = value;
744                                 break;
745                         case "attachdbfilename" :
746                         case "extended properties" :
747                         case "initial file name" :
748                                 parms.AttachDBFileName = value;
749                                 break;
750                         case "timeout" :
751                         case "connect timeout" :
752                         case "connection timeout" :
753                                 int tmpTimeout = ConvertToInt32 ("connect timeout", value,
754                                         DEFAULT_CONNECTIONTIMEOUT);
755                                 if (tmpTimeout < 0)
756                                         throw new ArgumentException ("Invalid 'connect timeout'. Must be an integer >=0 ");
757                                 else 
758                                         connectionTimeout = tmpTimeout;
759                                 break;
760                         case "connection lifetime" :
761                                 connectionLifeTime = ConvertToInt32 ("connection lifetime", value, DEFAULT_CONNECTIONLIFETIME);
762                                 break;
763                         case "connection reset" :
764                                 connectionReset = ConvertToBoolean ("connection reset", value, true);
765                                 break;
766                         case "language" :
767                         case "current language" :
768                                 parms.Language = value;
769                                 break;
770                         case "data source" :
771                         case "server" :
772                         case "address" :
773                         case "addr" :
774                         case "network address" :
775                                 dataSource = value;
776                                 break;
777                         case "encrypt":
778                                 if (ConvertToBoolean (name, value, false))
779                                         throw new NotImplementedException("SSL encryption for"
780                                                 + " data sent between client and server is not"
781                                                 + " implemented.");
782                                 break;
783                         case "enlist" :
784                                 if (!ConvertToBoolean (name, value, true))
785                                         throw new NotImplementedException("Disabling the automatic"
786                                                 + " enlistment of connections in the thread's current"
787                                                 + " transaction context is not implemented.");
788                                 break;
789                         case "initial catalog" :
790                         case "database" :
791                                 parms.Database = value;
792                                 break;
793                         case "integrated security" :
794                         case "trusted_connection" :
795                                 parms.DomainLogin = ConvertIntegratedSecurity(value);
796                                 break;
797                         case "max pool size" :
798                                 int tmpMaxPoolSize = ConvertToInt32 (name, value, DEFAULT_MAXPOOLSIZE);
799                                 if (tmpMaxPoolSize < MIN_MAXPOOLSIZE)
800                                         throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
801                                                 "Invalid '{0}'. The value must be greater than {1}.",
802                                                 name, MIN_MAXPOOLSIZE));
803                                 else
804                                         maxPoolSize = tmpMaxPoolSize;
805                                 break;
806                         case "min pool size" :
807                                 int tmpMinPoolSize = ConvertToInt32 (name, value, DEFAULT_MINPOOLSIZE);
808                                 if (tmpMinPoolSize < 0)
809                                         throw new ArgumentException ("Invalid 'min pool size'. Must be a integer >= 0");
810                                 else
811                                         minPoolSize = tmpMinPoolSize;
812                                 break;
813                         case "multipleactiveresultsets":
814                                 // FIXME: not implemented
815                                 ConvertToBoolean (name, value, false);
816                                 break;
817                         case "asynchronous processing" :
818                         case "async" :
819                                 async = ConvertToBoolean (name, value, false);
820                                 break;
821                         case "net" :
822                         case "network" :
823                         case "network library" :
824                                 if (!value.ToUpper ().Equals ("DBMSSOCN"))
825                                         throw new ArgumentException ("Unsupported network library.");
826                                 break;
827                         case "packet size" :
828                                 int tmpPacketSize = ConvertToInt32 (name, value, DEFAULT_PACKETSIZE);
829                                 if (tmpPacketSize < MIN_PACKETSIZE || tmpPacketSize > MAX_PACKETSIZE)
830                                         throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
831                                                 "Invalid 'Packet Size'. The value must be between {0} and {1}.",
832                                                 MIN_PACKETSIZE, MAX_PACKETSIZE));
833                                 else
834                                         packetSize = tmpPacketSize;
835                                 break;
836                         case "password" :
837                         case "pwd" :
838                                 parms.Password = new SecureString();
839                                 foreach (char c in value)
840                                         parms.Password.AppendChar(c);
841                                 parms.PasswordSet = true;
842                                 break;
843                         case "persistsecurityinfo" :
844                         case "persist security info" :
845                                 // FIXME : not implemented
846                                 // throw new NotImplementedException ();
847                                 break;
848                         case "pooling" :
849                                 pooling = ConvertToBoolean (name, value, true);
850                                 break;
851                         case "uid" :
852                         case "user" :
853                         case "user id" :
854                                 parms.User = value;
855                                 break;
856                         case "wsid" :
857                         case "workstation id" :
858                                 parms.Hostname = value;
859                                 break;
860                         case "user instance":
861                                 userInstance = ConvertToBoolean (name, value, false);
862                                 break;
863                         default :
864                                 throw new ArgumentException("Keyword not supported : '" + name + "'.");
865                         }
866                 }
867
868                 static bool IsValidDatabaseName (string database)
869                 {
870                         if ( database == null || database.Trim().Length == 0 || database.Length > 128)
871                                 return false ;
872                         
873                         if (database[0] == '"' && database[database.Length] == '"')
874                                 database = database.Substring (1, database.Length - 2);
875                         else if (Char.IsDigit (database[0]))
876                                 return false;
877
878                         if (database[0] == '_')
879                                 return false;
880
881                         foreach (char c  in database.Substring (1, database.Length - 1))
882                                 if (!Char.IsLetterOrDigit (c) && c != '_' && c != '-')
883                                         return false;
884                         return true;
885                 }
886
887                 private void OnSqlInfoMessage (SqlInfoMessageEventArgs value)
888                 {
889                         if (InfoMessage != null)
890                                 InfoMessage (this, value);
891                 }
892
893
894                 private sealed class SqlMonitorSocket : UdpClient
895                 {
896                         // UDP port that the SQL Monitor listens
897                         private static readonly int SqlMonitorUdpPort = 1434;
898                         //private static readonly string SqlServerNotExist = "SQL Server does not exist or access denied";
899
900                         private string server;
901                         private string instance;
902
903                         internal SqlMonitorSocket (string ServerName, string InstanceName) 
904                                 : base (ServerName, SqlMonitorUdpPort) 
905                         {
906                                 server = ServerName;
907                                 instance = InstanceName;
908                         }
909
910                         internal int DiscoverTcpPort (int timeoutSeconds)
911                         {
912                                 int SqlServerTcpPort;
913                                 Client.Blocking = false;
914                                 // send command to UDP 1434 (SQL Monitor) to get
915                                 // the TCP port to connect to the MS SQL server
916                                 ASCIIEncoding enc = new ASCIIEncoding ();
917                                 Byte[] rawrq = new Byte [instance.Length + 1];
918                                 rawrq[0] = 4;
919                                 enc.GetBytes (instance, 0, instance.Length, rawrq, 1);
920                                 Send (rawrq, rawrq.Length);
921
922                                 if (!Active)
923                                         return -1; // Error
924                                 
925                                 bool result;
926                                 long timeout = timeoutSeconds * 1000000;
927                                 result = Client.Poll ((int)timeout, SelectMode.SelectRead);
928                                 if (result == false)
929                                         return -1; // Error
930
931                                 if (Client.Available <= 0)
932                                         return -1; // Error
933                                 IPEndPoint endpoint = new IPEndPoint (Dns.GetHostEntry ("localhost").AddressList [0], 0);
934                                 Byte [] rawrs;
935
936                                 rawrs = Receive (ref endpoint);
937
938                                 string rs = Encoding.ASCII.GetString (rawrs);
939
940                                 string[] rawtokens = rs.Split (';');
941                                 Hashtable data = new Hashtable ();
942                                 for (int i = 0; i < rawtokens.Length / 2 && i < 256; i++) {
943                                         data [rawtokens [i * 2]] = rawtokens [ i * 2 + 1];
944                                 }
945
946                                 if (!data.ContainsKey ("tcp")) {
947                                         string msg = "Mono does not support names pipes or shared memory "
948                                                 + "for connecting to SQL Server. Please enable the TCP/IP "
949                                                 + "protocol.";
950                                         throw new NotImplementedException (msg);
951                                 }
952
953                                 SqlServerTcpPort = int.Parse ((string) data ["tcp"]);
954                                 Close ();
955
956                                 return SqlServerTcpPort;
957                         }
958                 }
959
960                 struct ColumnInfo
961                 {
962                         public string name;
963                         public Type type;
964
965                         public ColumnInfo (string name, Type type)
966                         {
967                                 this.name = name; this.type = type;
968                         }
969                 }
970
971                 static class ReservedWords
972                 {
973                         static readonly string [] reservedWords =
974                         {
975                                 "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER",
976                                   "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY",
977                                   "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE",
978                                   "PROCEDURE", "AUTHORIZATION", "FILLFACTOR", "PUBLIC",
979                                   "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ",
980                                   "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE",
981                                   "RECONFIGURE", "BROWSE", "FROM", "REFERENCES", "BULK",
982                                   "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE",
983                                   "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN",
984                                   "CHECK", "GROUP", "REVOKE", "CHECKPOINT", "HAVING", "RIGHT",
985                                   "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY",
986                                   "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL",
987                                   "COLLATE", "IDENTITYCOL", "RULE", "COLUMN", "IF", "SAVE",
988                                   "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT",
989                                   "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT",
990                                   "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER", "CONTINUE",
991                                   "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE",
992                                   "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER",
993                                   "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT",
994                                   "TEXTSIZE", "CURRENT_TIME", "LIKE", "THEN",
995                                   "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD",
996                                   "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK",
997                                   "TRANSACTION", "DBCC", "NONCLUSTERED", "TRIGGER",
998                                   "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL",
999                                   "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF",
1000                                   "UNIQUE", "DENY", "OFF", "UPDATE", "DESC", "OFFSETS",
1001                                   "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN",
1002                                   "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE",
1003                                   "OPENQUERY", "VARYING", "DROP", "OPENROWSET", "VIEW",
1004                                   "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN",
1005                                   "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL",
1006                                   "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT", "ABSOLUTE",
1007                                   "FOUND", "PRESERVE", "ACTION", "FREE", "PRIOR", "ADMIN",
1008                                   "GENERAL", "PRIVILEGES", "AFTER", "GET", "READS",
1009                                   "AGGREGATE", "GLOBAL", "REAL", "ALIAS", "GO", "RECURSIVE",
1010                                   "ALLOCATE", "GROUPING", "REF", "ARE", "HOST", "REFERENCING",
1011                                   "ARRAY", "HOUR", "RELATIVE", "ASSERTION", "IGNORE", "RESULT",
1012                                   "AT", "IMMEDIATE", "RETURNS", "BEFORE", "INDICATOR", "ROLE",
1013                                   "BINARY", "INITIALIZE", "ROLLUP", "BIT", "INITIALLY",
1014                                   "ROUTINE", "BLOB", "INOUT", "ROW", "BOOLEAN", "INPUT",
1015                                   "ROWS", "BOTH", "INT", "SAVEPOINT", "BREADTH", "INTEGER",
1016                                   "SCROLL", "CALL", "INTERVAL", "SCOPE", "CASCADED",
1017                                   "ISOLATION", "SEARCH", "CAST", "ITERATE", "SECOND",
1018                                   "CATALOG", "LANGUAGE", "SECTION", "CHAR", "LARGE",
1019                                   "SEQUENCE", "CHARACTER", "LAST", "SESSION", "CLASS",
1020                                   "LATERAL", "SETS", "CLOB", "LEADING", "SIZE", "COLLATION",
1021                                   "LESS", "SMALLINT", "COMPLETION", "LEVEL", "SPACE",
1022                                   "CONNECT", "LIMIT", "SPECIFIC", "CONNECTION", "LOCAL",
1023                                   "SPECIFICTYPE", "CONSTRAINTS", "LOCALTIME", "SQL",
1024                                   "CONSTRUCTOR", "LOCALTIMESTAMP", "SQLEXCEPTION",
1025                                   "CORRESPONDING", "LOCATOR", "SQLSTATE", "CUBE", "MAP",
1026                                   "SQLWARNING", "CURRENT_PATH", "MATCH", "START",
1027                                   "CURRENT_ROLE", "MINUTE", "STATE", "CYCLE", "MODIFIES",
1028                                   "STATEMENT", "DATA", "MODIFY", "STATIC", "DATE", "MODULE",
1029                                   "STRUCTURE", "DAY", "MONTH", "TEMPORARY", "DEC", "NAMES",
1030                                   "TERMINATE", "DECIMAL", "NATURAL", "THAN", "DEFERRABLE",
1031                                   "NCHAR", "TIME", "DEFERRED", "NCLOB", "TIMESTAMP", "DEPTH",
1032                                   "NEW", "TIMEZONE_HOUR", "DEREF", "NEXT", "TIMEZONE_MINUTE",
1033                                   "DESCRIBE", "NO", "TRAILING", "DESCRIPTOR", "NONE",
1034                                   "TRANSLATION", "DESTROY", "NUMERIC", "TREAT", "DESTRUCTOR",
1035                                   "OBJECT", "TRUE", "DETERMINISTIC", "OLD", "UNDER",
1036                                   "DICTIONARY", "ONLY", "UNKNOWN", "DIAGNOSTICS", "OPERATION",
1037                                   "UNNEST", "DISCONNECT", "ORDINALITY", "USAGE", "DOMAIN",
1038                                   "OUT", "USING", "DYNAMIC", "OUTPUT", "VALUE", "EACH",
1039                                   "PAD", "VARCHAR", "END-EXEC", "PARAMETER", "VARIABLE",
1040                                   "EQUALS", "PARAMETERS", "WHENEVER", "EVERY", "PARTIAL",
1041                                   "WITHOUT", "EXCEPTION", "PATH", "WORK", "EXTERNAL",
1042                                   "POSTFIX", "WRITE", "FALSE", "PREFIX", "YEAR", "FIRST",
1043                                   "PREORDER", "ZONE", "FLOAT", "PREPARE", "ADA", "AVG",
1044                                   "BIT_LENGTH", "CHAR_LENGTH", "CHARACTER_LENGTH", "COUNT",
1045                                   "EXTRACT", "FORTRAN", "INCLUDE", "INSENSITIVE", "LOWER",
1046                                   "MAX", "MIN", "OCTET_LENGTH", "OVERLAPS", "PASCAL",
1047                                   "POSITION", "SQLCA", "SQLCODE", "SQLERROR", "SUBSTRING",
1048                                   "SUM", "TRANSLATE", "TRIM", "UPPER"
1049                         };
1050                         static DataTable instance;
1051                         static public DataTable Instance {
1052                                 get {
1053                                         if (instance == null) {
1054                                                 DataRow row = null;
1055                                                 var newInstance = new DataTable ("ReservedWords");
1056                                                 newInstance.Columns.Add ("ReservedWord", typeof(string));
1057                                                 foreach (string reservedWord in reservedWords)
1058                                                 {
1059                                                         row = newInstance.NewRow();
1060
1061                                                         row["ReservedWord"] = reservedWord;
1062                                                         newInstance.Rows.Add(row);
1063                                                 }
1064                                                 instance = newInstance;
1065                                         }
1066                                         return instance;
1067                                 }
1068                         }
1069                 }
1070
1071                 static class MetaDataCollections
1072                 {
1073                         static readonly ColumnInfo [] columns = {
1074                                 new ColumnInfo ("CollectionName", typeof (string)),
1075                                 new ColumnInfo ("NumberOfRestrictions", typeof (int)),
1076                                 new ColumnInfo ("NumberOfIdentifierParts", typeof (int))
1077                         };
1078
1079                         static readonly object [][] rows = {
1080                                 new object [] {"MetaDataCollections", 0, 0},
1081                                 new object [] {"DataSourceInformation", 0, 0},
1082                                 new object [] {"DataTypes", 0, 0},
1083                                 new object [] {"Restrictions", 0, 0},
1084                                 new object [] {"ReservedWords", 0, 0},
1085                                 new object [] {"Users", 1, 1},
1086                                 new object [] {"Databases", 1, 1},
1087                                 new object [] {"Tables", 4, 3},
1088                                 new object [] {"Columns", 4, 4},
1089                                 new object [] {"StructuredTypeMembers", 4, 4},
1090                                 new object [] {"Views", 3, 3},
1091                                 new object [] {"ViewColumns", 4, 4},
1092                                 new object [] {"ProcedureParameters", 4, 1},
1093                                 new object [] {"Procedures", 4, 3},
1094                                 new object [] {"ForeignKeys", 4, 3},
1095                                 new object [] {"IndexColumns", 5, 4},
1096                                 new object [] {"Indexes", 4, 3},
1097                                 new object [] {"UserDefinedTypes", 2, 1}
1098                         };
1099
1100                         static DataTable instance;
1101                         static public DataTable Instance {
1102                                 get {
1103                                         if (instance == null) {
1104                                                 var newInstance = new DataTable ("MetaDataCollections");
1105                                                 foreach (ColumnInfo c in columns)
1106                                                         newInstance.Columns.Add (c.name, c.type);
1107                                                 foreach (object [] row in rows)
1108                                                         newInstance.LoadDataRow (row, true);
1109                                                 instance = newInstance;
1110                                         }
1111                                         return instance;
1112                                 }
1113                         }
1114                 }
1115
1116                 static class DataSourceInformation
1117                 {
1118                         static readonly ColumnInfo [] columns = {
1119                                 new ColumnInfo ("CompositeIdentifierSeparatorPattern", typeof (string)),
1120                                 new ColumnInfo ("DataSourceProductName", typeof(string)),
1121                                 new ColumnInfo ("DataSourceProductVersion", typeof(string)),
1122                                 new ColumnInfo ("DataSourceProductVersionNormalized", typeof(string)),
1123                                 new ColumnInfo ("GroupByBehavior", typeof(GroupByBehavior)),
1124                                 new ColumnInfo ("IdentifierPattern", typeof(string)),
1125                                 new ColumnInfo ("IdentifierCase", typeof(IdentifierCase)),
1126                                 new ColumnInfo ("OrderByColumnsInSelect", typeof(bool)),
1127                                 new ColumnInfo ("ParameterMarkerFormat", typeof(string)),
1128                                 new ColumnInfo ("ParameterMarkerPattern", typeof(string)),
1129                                 new ColumnInfo ("ParameterNameMaxLength", typeof(int)),
1130                                 new ColumnInfo ("ParameterNamePattern", typeof(string)),
1131                                 new ColumnInfo ("QuotedIdentifierPattern", typeof(string)),
1132                                 new ColumnInfo ("QuotedIdentifierCase", typeof(IdentifierCase)),
1133                                 new ColumnInfo ("StatementSeparatorPattern", typeof(string)),
1134                                 new ColumnInfo ("StringLiteralPattern", typeof(string)),
1135                                 new ColumnInfo ("SupportedJoinOperators", typeof(SupportedJoinOperators))
1136                         };
1137
1138                         static public DataTable GetInstance (SqlConnection conn)
1139                         {
1140                                 DataTable table = new DataTable ("DataSourceInformation");
1141                                 foreach (ColumnInfo c in columns)
1142                                         table.Columns.Add (c.name, c.type);
1143                                 DataRow row = table.NewRow ();
1144                                 row [0] = "\\.";
1145                                 row [1] = "Microsoft SQL Server";
1146                                 row [2] = conn.ServerVersion;;
1147                                 row [3] = conn.ServerVersion;;
1148                                 row [4] = GroupByBehavior.Unrelated;
1149                                 row [5] = @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)";
1150                                 row [6] = IdentifierCase.Insensitive; // FIXME: obtain this from SQL Server
1151                                 row [7] = false;
1152                                 row [8] = "{0}";
1153                                 row [9] = @"@[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)";
1154                                 row [10] = 128;
1155                                 row [11] = @"^[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)";
1156                                 row [12] = @"(([^\[]|\]\])*)";
1157                                 row [13] = IdentifierCase.Insensitive;  // FIXME: obtain this from SQL Server
1158                                 row [14] =";";
1159                                 row [15] = "'(([^']|'')*)'";
1160                                 row [16] = (SupportedJoinOperators.FullOuter | SupportedJoinOperators.Inner |
1161                                         SupportedJoinOperators.LeftOuter | SupportedJoinOperators.RightOuter);
1162                                 table.Rows.Add (row);
1163                                 return table;
1164                         }
1165                 }
1166
1167                 static class DataTypes
1168                 {
1169                         static readonly ColumnInfo [] columns = {
1170                                 new ColumnInfo ("TypeName", typeof(string)),
1171                                 new ColumnInfo ("ProviderDbType", typeof(int)),
1172                                 new ColumnInfo ("ColumnSize", typeof(long)),
1173                                 new ColumnInfo ("CreateFormat", typeof(string)),
1174                                 new ColumnInfo ("CreateParameters", typeof(string)),
1175                                 new ColumnInfo ("DataType", typeof(string)),
1176                                 new ColumnInfo ("IsAutoIncrementable", typeof(bool)),
1177                                 new ColumnInfo ("IsBestMatch", typeof(bool)),
1178                                 new ColumnInfo ("IsCaseSensitive", typeof(bool)),
1179                                 new ColumnInfo ("IsFixedLength", typeof(bool)),
1180                                 new ColumnInfo ("IsFixedPrecisionScale", typeof(bool)),
1181                                 new ColumnInfo ("IsLong", typeof(bool)),
1182                                 new ColumnInfo ("IsNullable", typeof(bool)),
1183                                 new ColumnInfo ("IsSearchable", typeof(bool)),
1184                                 new ColumnInfo ("IsSearchableWithLike", typeof(bool)),
1185                                 new ColumnInfo ("IsUnsigned", typeof(bool)),
1186                                 new ColumnInfo ("MaximumScale", typeof(short)),
1187                                 new ColumnInfo ("MinimumScale", typeof(short)),
1188                                 new ColumnInfo ("IsConcurrencyType", typeof(bool)),
1189                                 new ColumnInfo ("IsLiteralSupported", typeof(bool)),
1190                                 new ColumnInfo ("LiteralPrefix", typeof(string)),
1191                                 new ColumnInfo ("LiteralSuffix", typeof(string))
1192                         };
1193
1194                         static readonly object [][] rows = {
1195                                 new object [] {"smallint", 16, 5, "smallint", null, "System.Int16", true, true,
1196                                                false, true, true, false, true, true, false, false, null,
1197                                                null, false, null, null, null},
1198                                 new object [] {"int", 8, 10, "int", null, "System.Int32",
1199                                                true, true, false, true, true, false, true, true, false,
1200                                                false, null, null, false, null, null, null},
1201                                 new object [] {"real", 13, 7, "real", null,
1202                                                "System.Single", false, true, false, true, false, false,
1203                                                true, true, false, false, null, null, false, null, null, null},
1204                                 new object [] {"float", 6, 53, "float({0})",
1205                                                "number of bits used to store the mantissa", "System.Double",
1206                                                false, true, false, true, false, false, true, true,
1207                                                false, false, null, null, false, null, null, null},
1208                                 new object [] {"money", 9, 19, "money", null,
1209                                                "System.Decimal", false, false, false, true, true,
1210                                                false, true, true, false, false, null, null, false,
1211                                                null, null, null},
1212                                 new object [] {"smallmoney", 17, 10, "smallmoney", null,
1213                                                "System.Decimal", false, false, false, true, true, false,
1214                                                true, true, false, false, null, null, false, null, null, null},
1215                                 new object [] {"bit", 2, 1, "bit", null, "System.Boolean",
1216                                                false, false, false, true, false, false, true, true,
1217                                                false, null, null, null, false, null, null, null},
1218                                 new object [] {"tinyint", 20, 3, "tinyint", null,
1219                                                "System.SByte", true, true, false, true, true, false,
1220                                                true, true, false, true, null, null, false, null, null, null},
1221                                 new object [] {"bigint", 0, 19, "bigint", null,
1222                                                "System.Int64", true, true, false, true, true, false,
1223                                                true, true, false, false, null, null, false, null, null, null},
1224                                 new object [] {"timestamp", 19, 8, "timestamp", null,
1225                                                "System.Byte[]", false, false, false, true, false, false,
1226                                                false, true, false, null, null, null, true, null, "0x", null},
1227                                 new object [] {"binary", 1, 8000, "binary({0})", "length",
1228                                                "System.Byte[]", false, true, false, true, false, false,
1229                                                true, true, false, null, null, null, false, null, "0x", null},
1230                                 new object [] {"image", 7, 2147483647, "image", null,
1231                                                "System.Byte[]", false, true, false, false, false, true,
1232                                                true, false, false, null, null, null, false, null, "0x", null},
1233                                 new object [] {"text", 18, 2147483647, "text", null,
1234                                                "System.String", false, true, false, false, false, true,
1235                                                true, false, true, null, null, null, false, null, "'", "'"},
1236                                 new object [] {"ntext", 11, 1073741823, "ntext", null,
1237                                                "System.String", false, true, false, false, false, true,
1238                                                true, false, true, null, null, null, false, null, "N'", "'"},
1239                                 new object [] {"decimal", 5, 38, "decimal({0}, {1})",
1240                                                "precision,scale", "System.Decimal", true, true, false,
1241                                                true, false, false, true, true, false, false, 38, 0,
1242                                                false, null, null, null},
1243                                 new object [] {"numeric", 5, 38, "numeric({0}, {1})",
1244                                                "precision,scale", "System.Decimal", true, true, false,
1245                                                true, false, false, true, true, false, false, 38, 0,
1246                                                false, null, null, null},
1247                                 new object [] {"datetime", 4, 23, "datetime", null,
1248                                                "System.DateTime", false, true, false, true, false, false,
1249                                                true, true, true, null, null, null, false, null, "{ts '", "'}"},
1250                                 new object [] {"smalldatetime", 15, 16, "smalldatetime", null,
1251                                                "System.DateTime", false, true, false, true, false, false,
1252                                                true, true, true, null, null, null, false, null, "{ts '", "'}"},
1253                                 new object [] {"sql_variant", 23, null, "sql_variant",
1254                                                null, "System.Object", false, true, false, false, false,
1255                                                false, true, true, false, null, null, null, false, false,
1256                                                null, null},
1257                                 new object [] {"xml", 25, 2147483647, "xml", null,
1258                                                "System.String", false, false, false, false, false, true,
1259                                                true, false, false, null, null, null, false, false, null, null},
1260                                 new object [] {"varchar", 22, 2147483647, "varchar({0})",
1261                                                "max length", "System.String", false, true, false, false,
1262                                                false, false, true, true, true, null, null, null, false,
1263                                                null, "'", "'"},
1264                                 new object [] {"char", 3, 2147483647, "char({0})", "length",
1265                                                "System.String", false, true, false, true, false, false,
1266                                                true, true, true, null, null, null, false, null, "'", "'"},
1267                                 new object [] {"nchar", 10, 1073741823, "nchar({0})", "length",
1268                                                "System.String", false, true, false, true, false, false,
1269                                                true, true, true, null, null, null, false, null, "N'", "'"},
1270                                 new object [] {"nvarchar", 12, 1073741823, "nvarchar({0})", "max length",
1271                                                "System.String", false, true, false, false, false, false, true, true,
1272                                                true, null, null, null, false, null, "N'", "'"},
1273                                 new object [] {"varbinary", 21, 1073741823, "varbinary({0})",
1274                                                "max length", "System.Byte[]", false, true, false, false,
1275                                                false, false, true, true, false, null, null, null, false,
1276                                                null, "0x", null},
1277                                 new object [] {"uniqueidentifier", 14, 16, "uniqueidentifier", null,
1278                                                "System.Guid", false, true, false, true, false, false, true,
1279                                                true, false, null, null, null, false, null, "'", "'"},
1280                                 new object [] {"date", 31, 3L, "date", DBNull.Value,
1281                                                "System.DateTime", false, false, false, true, true, false,
1282                                                true, true, true, DBNull.Value, DBNull.Value, DBNull.Value,
1283                                                false, DBNull.Value, "{ts '", "'}"},
1284                                 new object [] {"time", 32, 5L, "time({0})", "scale",
1285                                                "System.TimeSpan", false, false, false, false, false, false,
1286                                                true, true, true, DBNull.Value, (short) 7, (short) 0,
1287                                                false, DBNull.Value, "{ts '", "'}"},
1288                                 new object [] {"datetime2", 33, 8L, "datetime2({0})", "scale",
1289                                                "System.DateTime", false, true, false, false, false, false,
1290                                                true, true, true, DBNull.Value, (short) 7, (short) 0,
1291                                                false, DBNull.Value, "{ts '", "'}"},
1292                                 new object [] {"datetimeoffset", 34, 10L, "datetimeoffset({0})",
1293                                                "scale", "System.DateTimeOffset", false, true, false, false,
1294                                                false, false, true, true, true, DBNull.Value, (short) 7, (short) 0,
1295                                                false, DBNull.Value, "{ts '", "'}"}
1296                         };
1297
1298                         static DataTable instance;
1299                         static public DataTable Instance {
1300                                 get {
1301                                         if (instance == null) {
1302                                                 instance = new DataTable ("DataTypes");
1303                                                 foreach (ColumnInfo c in columns)
1304                                                         instance.Columns.Add (c.name, c.type);
1305                                                 foreach (object [] row in rows)
1306                                                         instance.LoadDataRow (row, true);
1307                                         }
1308                                         return instance;
1309                                 }
1310                         }
1311                 }
1312
1313                 static class Restrictions
1314                 {
1315                         static readonly ColumnInfo [] columns = {
1316                                 new ColumnInfo ("CollectionName", typeof (string)),
1317                                 new ColumnInfo ("RestrictionName", typeof(string)),
1318                                 new ColumnInfo ("ParameterName", typeof(string)),
1319                                 new ColumnInfo ("RestrictionDefault", typeof(string)),
1320                                 new ColumnInfo ("RestrictionNumber", typeof(int))
1321                         };
1322
1323                         static readonly object [][] rows = {
1324                                 new object [] {"Users", "User_Name", "@Name", "name", 1},
1325                                 new object [] {"Databases", "Name", "@Name", "Name", 1},
1326
1327                                 new object [] {"Tables", "Catalog", "@Catalog", "TABLE_CATALOG", 1},
1328                                 new object [] {"Tables", "Owner", "@Owner", "TABLE_SCHEMA", 2},
1329                                 new object [] {"Tables", "Table", "@Name", "TABLE_NAME", 3},
1330                                 new object [] {"Tables", "TableType", "@TableType", "TABLE_TYPE", 4},
1331
1332                                 new object [] {"Columns", "Catalog", "@Catalog", "TABLE_CATALOG", 1},
1333                                 new object [] {"Columns", "Owner", "@Owner", "TABLE_SCHEMA", 2},
1334                                 new object [] {"Columns", "Table", "@Table", "TABLE_NAME", 3},
1335                                 new object [] {"Columns", "Column", "@Column", "COLUMN_NAME", 4},
1336
1337                                 new object [] {"StructuredTypeMembers", "Catalog", "@Catalog", "TYPE_CATALOG", 1},
1338                                 new object [] {"StructuredTypeMembers", "Owner", "@Owner", "TYPE_SCHEMA", 2},
1339                                 new object [] {"StructuredTypeMembers", "Type", "@Type", "TYPE_NAME", 3},
1340                                 new object [] {"StructuredTypeMembers", "Member", "@Member", "MEMBER_NAME", 4},
1341
1342                                 new object [] {"Views", "Catalog", "@Catalog", "TABLE_CATALOG", 1},
1343                                 new object [] {"Views", "Owner", "@Owner", "TABLE_SCHEMA", 2},
1344                                 new object [] {"Views", "Table", "@Table", "TABLE_NAME", 3},
1345
1346                                 new object [] {"ViewColumns", "Catalog", "@Catalog", "VIEW_CATALOG", 1},
1347                                 new object [] {"ViewColumns", "Owner", "@Owner", "VIEW_SCHEMA", 2},
1348                                 new object [] {"ViewColumns", "Table", "@Table", "VIEW_NAME", 3},
1349                                 new object [] {"ViewColumns", "Column", "@Column", "COLUMN_NAME", 4},
1350
1351                                 new object [] {"ProcedureParameters", "Catalog", "@Catalog", "SPECIFIC_CATALOG", 1},
1352                                 new object [] {"ProcedureParameters", "Owner", "@Owner", "SPECIFIC_SCHEMA", 2},
1353                                 new object [] {"ProcedureParameters", "Name", "@Name", "SPECIFIC_NAME", 3},
1354                                 new object [] {"ProcedureParameters", "Parameter", "@Parameter", "PARAMETER_NAME", 4},
1355
1356                                 new object [] {"Procedures", "Catalog", "@Catalog", "SPECIFIC_CATALOG", 1},
1357                                 new object [] {"Procedures", "Owner", "@Owner", "SPECIFIC_SCHEMA", 2},
1358                                 new object [] {"Procedures", "Name", "@Name", "SPECIFIC_NAME", 3},
1359                                 new object [] {"Procedures", "Type", "@Type", "ROUTINE_TYPE", 4},
1360
1361                                 new object [] {"IndexColumns", "Catalog", "@Catalog", "db_name()", 1},
1362                                 new object [] {"IndexColumns", "Owner", "@Owner", "user_name()", 2},
1363                                 new object [] {"IndexColumns", "Table", "@Table", "o.name", 3},
1364                                 new object [] {"IndexColumns", "ConstraintName", "@ConstraintName", "x.name", 4},
1365                                 new object [] {"IndexColumns", "Column", "@Column", "c.name", 5},
1366
1367                                 new object [] {"Indexes", "Catalog", "@Catalog", "db_name()", 1},
1368                                 new object [] {"Indexes", "Owner", "@Owner", "user_name()", 2},
1369                                 new object [] {"Indexes", "Table", "@Table", "o.name", 3},
1370                                 new object [] {"Indexes", "Name", "@Name", "x.name", 4},
1371
1372                                 new object [] {"UserDefinedTypes", "assembly_name", "@AssemblyName", "assemblies.name", 1},
1373                                 new object [] {"UserDefinedTypes", "udt_name", "@UDTName", "types.assembly_class", 2},
1374
1375                                 new object [] {"ForeignKeys", "Catalog", "@Catalog", "CONSTRAINT_CATALOG", 1},
1376                                 new object [] {"ForeignKeys", "Owner", "@Owner", "CONSTRAINT_SCHEMA", 2},
1377                                 new object [] {"ForeignKeys", "Table", "@Table", "TABLE_NAME", 3},
1378                                 new object [] {"ForeignKeys", "Name", "@Name", "CONSTRAINT_NAME", 4}
1379                         };
1380
1381                         static DataTable instance;
1382                         static public DataTable Instance {
1383                                 get {
1384                                         if (instance == null) {
1385                                                 instance = new DataTable ("Restrictions");
1386                                                 foreach (ColumnInfo c in columns)
1387                                                         instance.Columns.Add (c.name, c.type);
1388                                                 foreach (object [] row in rows)
1389                                                         instance.LoadDataRow (row, true);
1390                                         }
1391                                         return instance;
1392                                 }
1393                         }
1394                 }
1395
1396                 public override DataTable GetSchema ()
1397                 {
1398                         if (state == ConnectionState.Closed)
1399                                 throw ExceptionHelper.ConnectionClosed ();
1400
1401                         return MetaDataCollections.Instance;
1402                 }
1403
1404                 public override DataTable GetSchema (String collectionName)
1405                 {
1406                         return GetSchema (collectionName, null);
1407                 }
1408
1409                 public override DataTable GetSchema (String collectionName, string [] restrictionValues)
1410                 {
1411                         // LAMESPEC: In MS.NET, if collectionName is null, it throws ArgumentException.
1412
1413                         if (state == ConnectionState.Closed)
1414                                 throw ExceptionHelper.ConnectionClosed ();
1415
1416                         String cName = null;
1417                         DataTable schemaTable = MetaDataCollections.Instance;
1418                         int length = restrictionValues == null ? 0 : restrictionValues.Length;
1419
1420                         foreach (DataRow row in schemaTable.Rows) {
1421                                 if (String.Compare ((string) row["CollectionName"], collectionName, true) == 0) {
1422                                         if (length > (int) row["NumberOfRestrictions"]) {
1423                                                 throw new ArgumentException ("More restrictions were provided " +
1424                                                                              "than the requested schema ('" +
1425                                                                              row["CollectionName"].ToString () + "') supports");
1426                                         }
1427                                         cName = row["CollectionName"].ToString();
1428                                 }
1429                         }
1430
1431                         if (cName == null)
1432                                 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
1433                                         "The requested collection ({0}) is not defined.",
1434                                         collectionName));
1435
1436                         SqlCommand command     = null;
1437                         DataTable dataTable    = new DataTable ();
1438                         SqlDataAdapter dataAdapter = new SqlDataAdapter ();
1439
1440                         switch (cName)
1441                         {
1442                         case "Databases":
1443                                 command = new SqlCommand ("select name as database_name, dbid, crdate as create_date " +
1444                                                           "from master.sys.sysdatabases where (name = @Name or (@Name " +
1445                                                           "is null))", this);
1446                                 command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
1447                                 break;
1448                         case "ForeignKeys":
1449                                 command = new SqlCommand ("select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, " +
1450                                                           "TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE, " +
1451                                                           "IS_DEFERRABLE, INITIALLY_DEFERRED from " +
1452                                                           "INFORMATION_SCHEMA.TABLE_CONSTRAINTS where (CONSTRAINT_CATALOG" +
1453                                                           " = @Catalog or (@Catalog is null)) and (CONSTRAINT_SCHEMA = " +
1454                                                           "@Owner or (@Owner is null)) and (TABLE_NAME = @Table or (" +
1455                                                           "@Table is null)) and (CONSTRAINT_NAME = @Name or (@Name is null))" +
1456                                                           " and CONSTRAINT_TYPE = 'FOREIGN KEY' order by CONSTRAINT_CATALOG," +
1457                                                           " CONSTRAINT_SCHEMA, CONSTRAINT_NAME", this);
1458                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1459                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1460                                 command.Parameters.Add ("@Table", SqlDbType.NVarChar, 4000);
1461                                 command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
1462                                 break;
1463                         case "Indexes":
1464                                 command = new SqlCommand ("select distinct db_name() as constraint_catalog, " +
1465                                                           "constraint_schema = user_name (o.uid), " +
1466                                                           "constraint_name = x.name, table_catalog = db_name (), " +
1467                                                           "table_schema = user_name (o.uid), table_name = o.name, " +
1468                                                           "index_name  = x.name from sysobjects o, sysindexes x, " +
1469                                                           "sysindexkeys xk where o.type in ('U') and x.id = o.id and " +
1470                                                           "o.id = xk.id and x.indid = xk.indid and xk.keyno = x.keycnt " +
1471                                                           "and (db_name() = @Catalog or (@Catalog is null)) and " +
1472                                                           "(user_name() = @Owner or (@Owner is null)) and (o.name = " +
1473                                                           "@Table or (@Table is null)) and (x.name = @Name or (@Name is null))" +
1474                                                           "order by table_name, index_name", this);
1475                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1476                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1477                                 command.Parameters.Add ("@Table", SqlDbType.NVarChar, 4000);
1478                                 command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
1479                                 break;
1480                         case "IndexColumns":
1481                                 command = new SqlCommand ("select distinct db_name() as constraint_catalog, " +
1482                                                           "constraint_schema = user_name (o.uid), constraint_name = x.name, " +
1483                                                           "table_catalog = db_name (), table_schema = user_name (o.uid), " +
1484                                                           "table_name = o.name, column_name = c.name, " +
1485                                                           "ordinal_position = convert (int, xk.keyno), keyType = c.xtype, " +
1486                                                           "index_name = x.name from sysobjects o, sysindexes x, syscolumns c, " +
1487                                                           "sysindexkeys xk where o.type in ('U') and x.id = o.id and o.id = c.id " +
1488                                                           "and o.id = xk.id and x.indid = xk.indid and c.colid = xk.colid " +
1489                                                           "and xk.keyno <= x.keycnt and permissions (o.id, c.name) <> 0 " +
1490                                                           "and (db_name() = @Catalog or (@Catalog is null)) and (user_name() " +
1491                                                           "= @Owner or (@Owner is null)) and (o.name = @Table or (@Table is" +
1492                                                           " null)) and (x.name = @ConstraintName or (@ConstraintName is null)) " +
1493                                                           "and (c.name = @Column or (@Column is null)) order by table_name, " +
1494                                                           "index_name", this);
1495                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 8);
1496                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1497                                 command.Parameters.Add ("@Table", SqlDbType.NVarChar, 13);
1498                                 command.Parameters.Add ("@ConstraintName", SqlDbType.NVarChar, 4000);
1499                                 command.Parameters.Add ("@Column", SqlDbType.NVarChar, 4000);
1500                                 break;
1501                         case "Procedures":
1502                                 command = new SqlCommand ("select SPECIFIC_CATALOG, SPECIFIC_SCHEMA, SPECIFIC_NAME, " +
1503                                                           "ROUTINE_CATALOG, ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, " +
1504                                                           "CREATED, LAST_ALTERED from INFORMATION_SCHEMA.ROUTINES where " +
1505                                                           "(SPECIFIC_CATALOG = @Catalog or (@Catalog is null)) and " +
1506                                                           "(SPECIFIC_SCHEMA = @Owner or (@Owner is null)) and (SPECIFIC_NAME" +
1507                                                           " = @Name or (@Name is null)) and (ROUTINE_TYPE = @Type or (@Type " +
1508                                                           "is null)) order by SPECIFIC_CATALOG, SPECIFIC_SCHEMA, SPECIFIC_NAME", this);
1509                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1510                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1511                                 command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
1512                                 command.Parameters.Add ("@Type", SqlDbType.NVarChar, 4000);
1513                                 break;
1514                         case "ProcedureParameters":
1515                                 command = new SqlCommand ("select SPECIFIC_CATALOG, SPECIFIC_SCHEMA, SPECIFIC_NAME, " +
1516                                                           "ORDINAL_POSITION, PARAMETER_MODE, IS_RESULT, AS_LOCATOR, " +
1517                                                           "PARAMETER_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, " +
1518                                                           "CHARACTER_OCTET_LENGTH, COLLATION_CATALOG, COLLATION_SCHEMA, " +
1519                                                           "COLLATION_NAME, CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, " +
1520                                                           "CHARACTER_SET_NAME, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, " +
1521                                                           "NUMERIC_SCALE, DATETIME_PRECISION, INTERVAL_TYPE, " +
1522                                                           "INTERVAL_PRECISION from INFORMATION_SCHEMA.PARAMETERS where " +
1523                                                           "(SPECIFIC_CATALOG = @Catalog or (@Catalog is null)) and " +
1524                                                           "(SPECIFIC_SCHEMA = @Owner or (@Owner is null)) and (SPECIFIC_NAME = " +
1525                                                           "@Name or (@Name is null)) and (PARAMETER_NAME = @Parameter or (" +
1526                                                           "@Parameter is null)) order by SPECIFIC_CATALOG, SPECIFIC_SCHEMA," +
1527                                                           " SPECIFIC_NAME, PARAMETER_NAME", this);
1528                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1529                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1530                                 command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
1531                                 command.Parameters.Add ("@Parameter", SqlDbType.NVarChar, 4000);
1532                                 break;
1533                         case "Tables":
1534                                 command = new SqlCommand ("select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE " +
1535                                                           "from INFORMATION_SCHEMA.TABLES where" +
1536                                                           " (TABLE_CATALOG = @catalog or (@catalog is null)) and " +
1537                                                           "(TABLE_SCHEMA = @owner or (@owner is null))and " +
1538                                                           "(TABLE_NAME = @name or (@name is null)) and " +
1539                                                           "(TABLE_TYPE = @table_type or (@table_type is null))", this);
1540                                 command.Parameters.Add ("@catalog", SqlDbType.NVarChar, 8);
1541                                 command.Parameters.Add ("@owner", SqlDbType.NVarChar, 3);
1542                                 command.Parameters.Add ("@name", SqlDbType.NVarChar, 11);
1543                                 command.Parameters.Add ("@table_type", SqlDbType.NVarChar, 10);
1544                                 break;
1545                         case "Columns":
1546                                 command = new SqlCommand ("select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, " +
1547                                                           "ORDINAL_POSITION, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, " +
1548                                                           "CHARACTER_MAXIMUM_LENGTH, CHARACTER_OCTET_LENGTH, " +
1549                                                           "NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, " +
1550                                                           "DATETIME_PRECISION, CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, " +
1551                                                           "CHARACTER_SET_NAME, COLLATION_CATALOG from INFORMATION_SCHEMA.COLUMNS" +
1552                                                           " where (TABLE_CATALOG = @Catalog or (@Catalog is null)) and (" +
1553                                                           "TABLE_SCHEMA = @Owner or (@Owner is null)) and (TABLE_NAME = @table" +
1554                                                           " or (@Table is null)) and (COLUMN_NAME = @column or (@Column is null" +
1555                                                           ")) order by TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME", this);
1556                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1557                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1558                                 command.Parameters.Add ("@Table", SqlDbType.NVarChar, 4000);
1559                                 command.Parameters.Add ("@Column", SqlDbType.NVarChar, 4000);
1560                                 break;
1561                         case "Users":
1562                                 command = new SqlCommand ("select uid, name as user_name, createdate, updatedate from sysusers" +
1563                                                           " where (name = @Name or (@Name is null))", this);
1564                                 command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
1565                                 break;
1566                         case "StructuredTypeMembers":
1567                                 // Only available on SQL Server 2008
1568                                 // Running it again SQL 2005 results in the following exception:
1569                                 // Unable to build the 'StructuredTypeMembers' collection because
1570                                 // execution of the SQL query failed. See the inner exception for details.
1571                                 // ---> System.Data.SqlClient.SqlException: Invalid object name 'sys.table_types'.
1572                                 // 
1573                                 // I don't have access to SQL Server 2008 right now,
1574                                 // and can't find any online documentation on the 'sys.table_types'
1575                                 // view
1576                                 throw new NotImplementedException ();
1577                         case "Views":
1578                                 command = new SqlCommand ("select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CHECK_OPTION, " +
1579                                                           "IS_UPDATABLE from INFORMATION_SCHEMA.VIEWS where (TABLE_CATALOG" +
1580                                                           " = @Catalog or (@Catalog is null)) TABLE_SCHEMA = @Owner or " +
1581                                                           "(@Owner is null)) and (TABLE_NAME = @table or (@Table is null))" +
1582                                                           " order by TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME", this);
1583                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1584                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1585                                 command.Parameters.Add ("@Table", SqlDbType.NVarChar, 4000);
1586                                 break;
1587                         case "ViewColumns":
1588                                 command = new SqlCommand ("select VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME, TABLE_CATALOG, " +
1589                                                           "TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from " +
1590                                                           "INFORMATION_SCHEMA.VIEW_COLUMN_USAGE where (VIEW_CATALOG = " +
1591                                                           "@Catalog (@Catalog is null)) and (VIEW_SCHEMA = @Owner (@Owner" +
1592                                                           " is null)) and (VIEW_NAME = @Table or (@Table is null)) and " +
1593                                                           "(COLUMN_NAME = @Column or (@Column is null)) order by " +
1594                                                           "VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME", this);
1595                                 command.Parameters.Add ("@Catalog", SqlDbType.NVarChar, 4000);
1596                                 command.Parameters.Add ("@Owner", SqlDbType.NVarChar, 4000);
1597                                 command.Parameters.Add ("@Table", SqlDbType.NVarChar, 4000);
1598                                 command.Parameters.Add ("@Column", SqlDbType.NVarChar, 4000);
1599                                 break;
1600                         case "UserDefinedTypes":
1601                                 command = new SqlCommand ("select assemblies.name as assembly_name, types.assembly_class " +
1602                                                           "as udt_name, ASSEMBLYPROPERTY(assemblies.name, 'VersionMajor') " +
1603                                                           "as version_major, ASSEMBLYPROPERTY(assemblies.name, 'VersionMinor') " +
1604                                                           "as version_minor, ASSEMBLYPROPERTY(assemblies.name, 'VersionBuild') " +
1605                                                           "as version_build, ASSEMBLYPROPERTY(assemblies.name, 'VersionRevision') " +
1606                                                           "as version_revision, ASSEMBLYPROPERTY(assemblies.name, 'CultureInfo') " +
1607                                                           "as culture_info, ASSEMBLYPROPERTY(assemblies.name, 'PublicKey') " +
1608                                                           "as public_key, is_fixed_length, max_length, Create_Date, " +
1609                                                           "Permission_set_desc from sys.assemblies as assemblies join " +
1610                                                           "sys.assembly_types as types on assemblies.assembly_id = types.assembly_id" +
1611                                                           " where (assportemblies.name = @AssemblyName or (@AssemblyName is null)) and " +
1612                                                           "(types.assembly_class = @UDTName or (@UDTName is null))",
1613                                                           this);
1614                                 command.Parameters.Add ("@AssemblyName", SqlDbType.NVarChar, 4000);
1615                                 command.Parameters.Add ("@UDTName", SqlDbType.NVarChar, 4000);
1616                                 break;
1617                         case "MetaDataCollections":
1618                                 return MetaDataCollections.Instance;
1619                         case "DataSourceInformation":
1620                                 return DataSourceInformation.GetInstance (this);
1621                         case "DataTypes":
1622                                 return DataTypes.Instance;
1623                         case "ReservedWords":
1624                                 return ReservedWords.Instance;
1625                         case "Restrictions":
1626                                 return Restrictions.Instance;
1627                         }
1628                         for (int i = 0; i < length; i++) {
1629                                 command.Parameters[i].Value = restrictionValues[i];
1630                         }
1631                         dataAdapter.SelectCommand = command;
1632                         dataAdapter.Fill (dataTable);
1633                         return dataTable;
1634                 }
1635                 
1636                 public static void ChangePassword (string connectionString, string newPassword)
1637                 {
1638                         if (String.IsNullOrEmpty (connectionString))
1639                                 throw new ArgumentNullException ("The 'connectionString' cannot be null or empty.");
1640                         if (String.IsNullOrEmpty (newPassword))
1641                                 throw new ArgumentNullException ("The 'newPassword' cannot be null or empty.");
1642                         if (newPassword.Length > 128)
1643                                 throw new ArgumentException ("The length of 'newPassword' cannot exceed 128 characters.");
1644                         using (SqlConnection conn = new SqlConnection (connectionString)) {
1645                                 conn.Open ();
1646                                 conn.tds.Execute (String.Format ("sp_password '{0}', '{1}', '{2}'",
1647                                                                  conn.parms.Password, newPassword, conn.parms.User));
1648                         }
1649                 }
1650
1651                 public static void ClearAllPools ()
1652                 {
1653                         // FIXME: locking
1654                         IDictionary pools = SqlConnection.sqlConnectionPools.GetConnectionPool ();
1655                         foreach (TdsConnectionPool pool in pools.Values) {
1656                                 if (pool != null)
1657                                         pool.ResetConnectionPool ();
1658                         }
1659                         pools.Clear ();
1660                 }
1661
1662                 public static void ClearPool (SqlConnection connection)
1663                 {
1664                         if (connection == null)
1665                                 throw new ArgumentNullException ("connection");
1666
1667                         // FIXME: locking
1668                         if (connection.pooling) {
1669                                 TdsConnectionPool pool = sqlConnectionPools.GetConnectionPool (connection.ConnectionString);
1670                                 if (pool != null)
1671                                         pool.ResetConnectionPool ();
1672                         }
1673                 }
1674
1675                 [MonoTODO]
1676                 public void ResetStatistics ()
1677                 {
1678                         throw new NotImplementedException ();
1679                 }
1680
1681                 [MonoTODO]
1682                 public IDictionary RetrieveStatistics ()
1683                 {
1684                         throw new NotImplementedException ();
1685                 }
1686
1687                 #endregion // Methods
1688
1689                 #region Fields Net 2
1690
1691                 bool async;
1692                 bool userInstance;
1693
1694                 #endregion // Fields  Net 2
1695
1696                 #region Properties Net 2
1697
1698                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
1699                 internal bool AsyncProcessing  {
1700                         get { return async; }
1701                 }
1702
1703                 #endregion // Properties Net 2
1704
1705
1706         }
1707 }