2005-07-26 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlConnection.cs
1 // created on 10/5/2002 at 23:01
2 // Npgsql.NpgsqlConnection.cs
3 //
4 // Author:
5 //      Francisco Jr. (fxjrlists@yahoo.com.br)
6 //
7 //      Copyright (C) 2002 The Npgsql Development Team
8 //      npgsql-general@gborg.postgresql.org
9 //      http://gborg.postgresql.org/project/npgsql/projdisplay.php
10 //
11 //
12 // This library is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU Lesser General Public
14 // License as published by the Free Software Foundation; either
15 // version 2.1 of the License, or (at your option) any later version.
16 //
17 // This library is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 // Lesser General Public License for more details.
21 //
22 // You should have received a copy of the GNU Lesser General Public
23 // License along with this library; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
26 using System;
27 using System.ComponentModel;
28 using System.Data;
29 using System.Text;
30 using System.Collections;
31 using System.Resources;
32 using System.Security.Cryptography;
33 using System.Security.Cryptography.X509Certificates;
34
35 using Mono.Security.Protocol.Tls;
36
37 using NpgsqlTypes;
38
39 #if WITHDESIGN
40 using Npgsql.Design;
41 #endif
42
43 namespace Npgsql
44 {
45     /// <summary>
46     /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notice</see> events.
47     /// </summary>
48     /// <param name="e">A <see cref="Npgsql.NpgsqlNoticeEventArgs">NpgsqlNoticeEventArgs</see> that contains the event data.</param>
49     public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e);
50
51     /// <summary>
52     /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notification</see> events.
53     /// </summary>
54     /// <param name="sender">The source of the event.</param>
55     /// <param name="e">A <see cref="Npgsql.NpgsqlNotificationEventArgs">NpgsqlNotificationEventArgs</see> that contains the event data.</param>
56     public delegate void NotificationEventHandler(Object sender, NpgsqlNotificationEventArgs e);
57
58     /// <summary>
59     /// This class represents a connection to a
60     /// PostgreSQL server.
61     /// </summary>
62     #if WITHDESIGN
63     [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))]
64     #endif
65     
66     public sealed class NpgsqlConnection : Component, IDbConnection, ICloneable
67     {
68         // Logging related values
69         private static readonly String CLASSNAME = "NpgsqlConnection";
70         private static ResourceManager resman = new System.Resources.ResourceManager(typeof(NpgsqlConnection));
71
72         /// <summary>
73         /// Occurs on NoticeResponses from the PostgreSQL backend.
74         /// </summary>
75         public event NoticeEventHandler                            Notice;
76         internal NoticeEventHandler                    NoticeDelegate;
77
78         /// <summary>
79         /// Occurs on NotificationResponses from the PostgreSQL backend.
80         /// </summary>
81         public event NotificationEventHandler          Notification;
82         internal NotificationEventHandler              NotificationDelegate;
83
84         /// <summary>
85         /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate.
86         /// </summary>
87         public event CertificateSelectionCallback      CertificateSelectionCallback;
88         internal CertificateSelectionCallback          CertificateSelectionCallbackDelegate;
89
90         /// <summary>
91         /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
92         /// </summary>
93         public event CertificateValidationCallback     CertificateValidationCallback;
94         internal CertificateValidationCallback         CertificateValidationCallbackDelegate;
95
96         /// <summary>
97         /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate.
98         /// </summary>
99         public event PrivateKeySelectionCallback       PrivateKeySelectionCallback;
100         internal PrivateKeySelectionCallback           PrivateKeySelectionCallbackDelegate;
101
102         // Set this when disposed is called.
103         private bool                                   disposed = false;
104
105         // Connection string values.
106         private NpgsqlConnectionString                 connection_string;
107
108         // Connector being used for the active connection.
109         private NpgsqlConnector                        connector = null;
110
111
112         /// <summary>
113         /// Initializes a new instance of the
114         /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class.
115         /// </summary>
116         public NpgsqlConnection() : this(String.Empty)
117         {}
118
119         /// <summary>
120         /// Initializes a new instance of the
121         /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class
122         /// and sets the <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
123         /// </summary>
124         /// <param name="ConnectionString">The connection used to open the PostgreSQL database.</param>
125         public NpgsqlConnection(String ConnectionString)
126         {
127             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, "NpgsqlConnection()");
128             
129             connection_string = NpgsqlConnectionString.ParseConnectionString(ConnectionString);
130             LogConnectionString();
131
132             NoticeDelegate = new NoticeEventHandler(OnNotice);
133             NotificationDelegate = new NotificationEventHandler(OnNotification);
134
135             CertificateValidationCallbackDelegate = new CertificateValidationCallback(DefaultCertificateValidationCallback);
136             CertificateSelectionCallbackDelegate = new CertificateSelectionCallback(DefaultCertificateSelectionCallback);
137             PrivateKeySelectionCallbackDelegate = new PrivateKeySelectionCallback(DefaultPrivateKeySelectionCallback);
138         }
139
140         /// <summary>
141         /// Gets or sets the string used to connect to a PostgreSQL database.
142         /// Valid values are:
143         /// Server:        Address/Name of Postgresql Server;
144         /// Port:          Port to connect to;
145         /// Protocol:      Protocol version to use, instead of automatic; Integer 2 or 3;
146         /// Database:      Database name. Defaults to user name if not specified;
147         /// User:          User name;
148         /// Password:      Password for clear text authentication;
149         /// SSL:           True or False. Controls whether to attempt a secure connection. Default = False;
150         /// Pooling:       True or False. Controls whether connection pooling is used. Default = True;
151         /// MinPoolSize:   Min size of connection pool;
152         /// MaxPoolSize:   Max size of connection pool;
153         /// Encoding:      Encoding to be used;
154         /// Timeout:       Time to wait for connection open in seconds.
155         /// </summary>
156         /// <value>The connection string that includes the server name,
157         /// the database name, and other parameters needed to establish
158         /// the initial connection. The default value is an empty string.
159         /// </value>
160         
161         #if WITHDESIGN
162         [RefreshProperties(RefreshProperties.All), DefaultValue(""), RecommendedAsConfigurable(true)]
163         [NpgsqlSysDescription("Description_ConnectionString", typeof(NpgsqlConnection)), Category("Data")]
164         [Editor(typeof(ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
165         #endif
166         
167         public String ConnectionString {
168             get
169             {
170                 return connection_string.ToString();
171             }
172             set
173             {
174                 // Connection string is used as the key to the connector.  Because of this,
175                 // we cannot change it while we own a connector.
176                 CheckConnectionClosed();
177                 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value);
178                 connection_string = NpgsqlConnectionString.ParseConnectionString(value);
179                 LogConnectionString();
180             }
181         }
182
183         /// <summary>
184         /// Backend server host name.
185         /// </summary>
186         [Browsable(true)]
187         public String Host {
188             get
189             {
190                 return connection_string.ToString(ConnectionStringKeys.Host);
191             }
192         }
193
194         /// <summary>
195         /// Backend server port.
196         /// </summary>
197         [Browsable(true)]
198         public Int32 Port {
199             get
200             {
201                 return connection_string.ToInt32(ConnectionStringKeys.Port, ConnectionStringDefaults.Port);
202             }
203         }
204
205         /// <summary>
206         /// If true, the connection will attempt to use SSL.
207         /// </summary>
208         [Browsable(true)]
209         public Boolean SSL {
210             get
211             {
212                 return connection_string.ToBool(ConnectionStringKeys.SSL);
213             }
214         }
215
216         /// <summary>
217         /// Gets the time to wait while trying to establish a connection
218         /// before terminating the attempt and generating an error.
219         /// </summary>
220         /// <value>The time (in seconds) to wait for a connection to open. The default value is 15 seconds.</value>
221         
222         #if WITHDESIGN
223         [NpgsqlSysDescription("Description_ConnectionTimeout", typeof(NpgsqlConnection))]
224         #endif
225         
226         public Int32 ConnectionTimeout {
227             get
228             {
229                 return connection_string.ToInt32(ConnectionStringKeys.Timeout, ConnectionStringDefaults.Timeout);
230             }
231         }
232
233         ///<summary>
234         /// Gets the name of the current database or the database to be used after a connection is opened.
235         /// </summary>
236         /// <value>The name of the current database or the name of the database to be
237         /// used after a connection is opened. The default value is the empty string.</value>
238         #if WITHDESIGN
239         [NpgsqlSysDescription("Description_Database", typeof(NpgsqlConnection))]
240         #endif
241         
242         public String Database {
243             get
244             {
245                 return connection_string.ToString(ConnectionStringKeys.Database);
246             }
247         }
248
249         /// <summary>
250         /// Gets the current state of the connection.
251         /// </summary>
252         /// <value>A bitwise combination of the <see cref="System.Data.ConnectionState">ConnectionState</see> values. The default is <b>Closed</b>.</value>
253         [Browsable(false)]
254         public ConnectionState State {
255             get
256             {
257                 CheckNotDisposed();
258
259                 if (connector != null)
260                 {
261                     return connector.State;
262                 }
263                 else
264                 {
265                     return ConnectionState.Closed;
266                 }
267             }
268         }
269
270         /// <summary>
271         /// Version of the PostgreSQL backend.
272         /// This can only be called when there is an active connection.
273         /// </summary>
274         [Browsable(false)]
275         public ServerVersion ServerVersion {
276             get
277             {
278                 CheckConnectionOpen();
279                 return connector.ServerVersion;
280             }
281         }
282
283         /// <summary>
284         /// Protocol version in use.
285         /// This can only be called when there is an active connection.
286         /// </summary>
287         [Browsable(false)]
288         public ProtocolVersion BackendProtocolVersion {
289             get
290             {
291                 CheckConnectionOpen();
292                 return connector.BackendProtocolVersion;
293             }
294         }
295
296         /// <summary>
297         /// Begins a database transaction.
298         /// </summary>
299         /// <returns>An <see cref="System.Data.IDbTransaction">IDbTransaction</see>
300         /// object representing the new transaction.</returns>
301         /// <remarks>
302         /// Currently there's no support for nested transactions.
303         /// </remarks>
304         IDbTransaction IDbConnection.BeginTransaction()
305         {
306             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.BeginTransaction");
307
308             return BeginTransaction();
309         }
310
311         /// <summary>
312         /// Begins a database transaction with the specified isolation level.
313         /// </summary>
314         /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param>
315         /// <returns>An <see cref="System.Data.IDbTransaction">IDbTransaction</see>
316         /// object representing the new transaction.</returns>
317         /// <remarks>
318         /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
319         /// There's no support for nested transactions.
320         /// </remarks>
321         IDbTransaction IDbConnection.BeginTransaction(IsolationLevel level)
322         {
323             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.BeginTransaction", level);
324
325             return BeginTransaction(level);
326         }
327
328         /// <summary>
329         /// Begins a database transaction.
330         /// </summary>
331         /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
332         /// object representing the new transaction.</returns>
333         /// <remarks>
334         /// Currently there's no support for nested transactions.
335         /// </remarks>
336         public NpgsqlTransaction BeginTransaction()
337         {
338             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction");
339             return this.BeginTransaction(IsolationLevel.ReadCommitted);
340         }
341
342         /// <summary>
343         /// Begins a database transaction with the specified isolation level.
344         /// </summary>
345         /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param>
346         /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
347         /// object representing the new transaction.</returns>
348         /// <remarks>
349         /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
350         /// There's no support for nested transactions.
351         /// </remarks>
352         public NpgsqlTransaction BeginTransaction(IsolationLevel level)
353         {
354             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction", level);
355
356             CheckConnectionOpen();
357
358             if (connector.Transaction != null)
359             {
360                 throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions"));
361             }
362
363             return new NpgsqlTransaction(this, level);
364         }
365
366         /// <summary>
367         /// Opens a database connection with the property settings specified by the
368         /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
369         /// </summary>
370         public void Open()
371         {
372             CheckConnectionClosed();
373
374             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
375
376             // Check if there is any missing argument.
377             if (! connection_string.Contains(ConnectionStringKeys.Host))
378                 throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.Host);
379             if (! connection_string.Contains(ConnectionStringKeys.UserName))
380                 throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.UserName);
381
382             // Get a Connector.  The connector returned is guaranteed to be connected and ready to go.
383             connector = NpgsqlConnectorPool.ConnectorPoolMgr.RequestConnector (this);
384
385             connector.Notice += NoticeDelegate;
386             connector.Notification += NotificationDelegate;
387         }
388
389         /// <summary>
390         /// This method changes the current database by disconnecting from the actual
391         /// database and connecting to the specified.
392         /// </summary>
393         /// <param name="dbName">The name of the database to use in place of the current database.</param>
394         public void ChangeDatabase(String dbName)
395         {
396             CheckNotDisposed();
397
398             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeDatabase", dbName);
399
400             if (dbName == null)
401                 throw new ArgumentNullException("dbName");
402
403             if (dbName == String.Empty)
404                 throw new ArgumentOutOfRangeException(String.Format(resman.GetString("Exception_InvalidDbName"), dbName), "dbName");
405
406             String oldDatabaseName = Database;
407
408             Close();
409
410             connection_string[ConnectionStringKeys.Database] = dbName;
411
412             Open();
413         }
414
415         /// <summary>
416         /// Releases the connection to the database.  If the connection is pooled, it will be
417         ///     made available for re-use.  If it is non-pooled, the actual connection will be shutdown.
418         /// </summary>
419         public void Close()
420         {
421             if (!disposed)
422             {
423                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");
424
425                 if (connector != null)
426                 {
427
428                     connector.Notification -= NotificationDelegate;
429                     connector.Notice -= NoticeDelegate;
430
431                     NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector);
432                     connector = null;
433                 }
434             }
435         }
436
437         /// <summary>
438         /// Creates and returns a <see cref="System.Data.IDbCommand">IDbCommand</see>
439         /// object associated with the <see cref="System.Data.IDbConnection">IDbConnection</see>.
440         /// </summary>
441         /// <returns>A <see cref="System.Data.IDbCommand">IDbCommand</see> object.</returns>
442         IDbCommand IDbConnection.CreateCommand()
443         {
444             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.CreateCommand");
445             return (NpgsqlCommand) CreateCommand();
446         }
447
448         /// <summary>
449         /// Creates and returns a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>
450         /// object associated with the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
451         /// </summary>
452         /// <returns>A <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> object.</returns>
453         public NpgsqlCommand CreateCommand()
454         {
455             CheckNotDisposed();
456
457             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateCommand");
458             return new NpgsqlCommand("", this);
459         }
460
461         /// <summary>
462         /// Releases all resources used by the
463         /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
464         /// </summary>
465         /// <param name="disposing"><b>true</b> when called from Dispose();
466         /// <b>false</b> when being called from the finalizer.</param>
467         protected override void Dispose(bool disposing)
468         {
469
470             if (!disposed)
471             {
472                 if (disposing)
473                 {
474                     NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
475                     Close();
476                 }
477                 else
478                 {
479                     if (State != ConnectionState.Closed)
480                     {
481                         NpgsqlEventLog.LogMsg(resman, "Log_ConnectionLeaking", LogLevel.Debug);
482                         NpgsqlConnectorPool.ConnectorPoolMgr.FixPoolCountBecauseOfConnectionDisposeFalse(this);
483                     }
484                 }
485
486                 base.Dispose (disposing);
487                 disposed = true;
488
489             }
490         }
491
492         /// <summary>
493         /// Create a new connection based on this one.
494         /// </summary>
495         /// <returns>A new NpgsqlConnection object.</returns>
496         Object ICloneable.Clone()
497         {
498             return Clone();
499         }
500
501         /// <summary>
502         /// Create a new connection based on this one.
503         /// </summary>
504         /// <returns>A new NpgsqlConnection object.</returns>
505         public NpgsqlConnection Clone()
506         {
507             CheckNotDisposed();
508
509             NpgsqlConnection C = new NpgsqlConnection(ConnectionString);
510
511             C.Notice += this.Notice;
512
513             if (connector != null)
514             {
515                 C.Open();
516             }
517
518             return C;
519         }
520
521         //
522         // Internal methods and properties
523         //
524         internal void OnNotice(object O, NpgsqlNoticeEventArgs E)
525         {
526             if (Notice != null)
527             {
528                 Notice(this, E);
529             }
530         }
531
532         internal void OnNotification(object O, NpgsqlNotificationEventArgs E)
533         {
534             if (Notification != null)
535             {
536                 Notification(this, E);
537             }
538         }
539
540         /// <summary>
541         /// The connector object connected to the backend.
542         /// </summary>
543         internal NpgsqlConnector Connector
544         {
545             get
546             {
547                 return connector;
548             }
549         }
550
551         /// <summary>
552         /// Gets the NpgsqlConnectionString containing the parsed connection string values.
553         /// </summary>
554         internal NpgsqlConnectionString ConnectionStringValues {
555             get
556             {
557                 return connection_string;
558             }
559         }
560
561         /// <summary>
562         /// User name.
563         /// </summary>
564         internal String UserName {
565             get
566             {
567                 return connection_string.ToString(ConnectionStringKeys.UserName);
568             }
569         }
570
571         /// <summary>
572         /// Password.
573         /// </summary>
574         internal String Password {
575             get
576             {
577                 return connection_string.ToString(ConnectionStringKeys.Password);
578             }
579         }
580
581         /// <summary>
582         /// Determine if connection pooling will be used for this connection.
583         /// </summary>
584         internal Boolean Pooling {
585             get
586             {
587                 return (
588                            connection_string.ToBool(ConnectionStringKeys.Pooling, ConnectionStringDefaults.Pooling) &&
589                            connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, ConnectionStringDefaults.MaxPoolSize) > 0
590                        );
591             }
592         }
593
594         internal Int32 MinPoolSize {
595             get
596             {
597                 return connection_string.ToInt32(ConnectionStringKeys.MinPoolSize, 0, MaxPoolSize, ConnectionStringDefaults.MinPoolSize);
598             }
599         }
600
601         internal Int32 MaxPoolSize {
602             get
603             {
604                 return connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, 0, 1024, ConnectionStringDefaults.MaxPoolSize);
605             }
606         }
607
608         internal Int32 Timeout {
609             get
610             {
611                 return connection_string.ToInt32(ConnectionStringKeys.Timeout, 0, 1024, ConnectionStringDefaults.Timeout);
612             }
613         }
614
615
616
617         //
618         // Event handlers
619         //
620
621         /// <summary>
622         /// Default SSL CertificateSelectionCallback implementation.
623         /// </summary>
624         internal X509Certificate DefaultCertificateSelectionCallback(
625             X509CertificateCollection      clientCertificates,
626             X509Certificate                serverCertificate,
627             string                         targetHost,
628             X509CertificateCollection      serverRequestedCertificates)
629         {
630             if (CertificateSelectionCallback != null)
631             {
632                 return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
633             }
634             else
635             {
636                 return null;
637             }
638         }
639
640         /// <summary>
641         /// Default SSL CertificateValidationCallback implementation.
642         /// </summary>
643         internal bool DefaultCertificateValidationCallback(
644             X509Certificate       certificate,
645             int[]                 certificateErrors)
646         {
647             if (CertificateValidationCallback != null)
648             {
649                 return CertificateValidationCallback(certificate, certificateErrors);
650             }
651             else
652             {
653                 return true;
654             }
655         }
656
657         /// <summary>
658         /// Default SSL PrivateKeySelectionCallback implementation.
659         /// </summary>
660         internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(
661             X509Certificate                certificate,
662             string                         targetHost)
663         {
664             if (PrivateKeySelectionCallback != null)
665             {
666                 return PrivateKeySelectionCallback(certificate, targetHost);
667             }
668             else
669             {
670                 return null;
671             }
672         }
673
674
675
676         //
677         // Private methods and properties
678         //
679
680
681         /// <summary>
682         /// Write each key/value pair in the connection string to the log.
683         /// </summary>
684         private void LogConnectionString()
685         {
686             foreach (DictionaryEntry DE in connection_string)
687             {
688                 NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, DE.Key, DE.Value);
689             }
690         }
691
692         private void CheckConnectionOpen()
693         {
694             if (disposed)
695             {
696                 throw new ObjectDisposedException(CLASSNAME);
697             }
698
699             if (connector == null)
700             {
701                 throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen"));
702             }
703         }
704
705         private void CheckConnectionClosed()
706         {
707             if (disposed)
708             {
709                 throw new ObjectDisposedException(CLASSNAME);
710             }
711
712             if (connector != null)
713             {
714                 throw new InvalidOperationException(resman.GetString("Exception_ConnOpen"));
715             }
716         }
717
718         private void CheckNotDisposed()
719         {
720             if (disposed)
721             {
722                 throw new ObjectDisposedException(CLASSNAME);
723             }
724         }
725         
726         
727         /// <summary>
728         /// This method returns the collections we support.
729         /// <summary>
730         public DataTable GetSchema()
731         {
732             
733             
734             DataTable result = new DataTable("Schema");
735             
736             result.Columns.Add ("CollectionName", typeof (string));
737             result.Columns.Add ("NumberOfRestrictions", typeof (int));
738             
739             
740             DataRow row = result.NewRow();
741             row["CollectionName"] = "Databases";
742             row["NumberOfRestrictions"] = 0;
743             
744             result.Rows.Add(row);
745             
746             return result;
747         }
748         
749         /// <summary>
750         /// This method returns the collection data.
751         ///
752         /// <summary>
753         public DataTable GetSchema(String CollectionName)
754         {
755            
756             if (CollectionName == "Databases")
757                 return GetSchemaDatabases();
758             
759             throw new NotSupportedException(); 
760             
761         }
762         
763         
764         private DataTable GetSchemaDatabases()
765         {
766             NpgsqlConnection conn = new NpgsqlConnection(this.ConnectionString);
767             conn.Open();
768             NpgsqlCommand c = new NpgsqlCommand("SELECT d.datname as \"Name\", u.usename as \"Owner\", pg_catalog.pg_encoding_to_char(d.encoding) as \"Encoding\" FROM pg_catalog.pg_database d LEFT JOIN pg_catalog.pg_user u ON d.datdba = u.usesysid ORDER BY 1;", conn);
769             
770             
771             DataTable result = new DataTable("Databases");
772             
773             result.Columns.Add ("Name", typeof (string));
774             result.Columns.Add ("Owner", typeof (string));
775             result.Columns.Add ("Encoding", typeof (string));
776             
777             
778             NpgsqlDataReader dr = c.ExecuteReader();
779                         
780             while (dr.Read())
781             {
782                         
783                 DataRow row = result.NewRow();
784                 row["Name"] = dr["Name"];
785                 row["Owner"] = dr["Owner"];
786                 row["Encoding"] = dr["Encoding"];
787                 
788                 result.Rows.Add(row);
789             }
790             
791             
792             dr.Close();
793             
794             conn.Close();
795             
796
797             return result;
798         }
799         
800         
801         
802
803     }
804
805
806
807 }