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