Kaynağa Gözat

2004-08-25 Francisco Figueiredo Jr. <[email protected]>

	* Updated files from gborg cvs. Added support for geometric types.
	Improved Unicode encoding support. It was possible to characters in unicode cross the stream buffer and be discarded. Now we use Decoders which handle that situation. Thanks Oskars Ozols (oskars dot ozols at di dot lv)
        Improved connection startup code. Now new connections in ms.net goes a lot faster. Thanks Mikko Korkalo (mikko.korkalo at f-solutions dot net) for the patch.
		Fixed a problem with plan execution in 7.3 backend versions. The text parameters weren't being quoted and backend complained about that.

		- gborg 898: Added HasRows method to NpgsqlDataReader. Thanks [email protected] for feature request.
		- gborg 899: Fixed return of IsClosed after closing NpgsqlDataReader. Thanks Mikko Korkalo (mikko.korkalo at f-solutions dot net) for the patch.

svn path=/trunk/mcs/; revision=32851
Francisco Figueiredo Jr. 21 yıl önce
ebeveyn
işleme
f9108612e3
38 değiştirilmiş dosya ile 3973 ekleme ve 1644 silme
  1. 4 0
      mcs/class/Npgsql/Npgsql.dll.sources
  2. 12 12
      mcs/class/Npgsql/Npgsql/Design/ConnectionStringEditorForm.cs
  3. 60 14
      mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs
  4. 1 1
      mcs/class/Npgsql/Npgsql/NpgsqlBinaryRow.cs
  5. 40 26
      mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs
  6. 103 65
      mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs
  7. 2 2
      mcs/class/Npgsql/Npgsql/NpgsqlConnectedState.cs
  8. 255 645
      mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs
  9. 0 3
      mcs/class/Npgsql/Npgsql/NpgsqlConnection.de.resx
  10. 0 3
      mcs/class/Npgsql/Npgsql/NpgsqlConnection.es.resx
  11. 0 3
      mcs/class/Npgsql/Npgsql/NpgsqlConnection.fi.resx
  12. 3 18
      mcs/class/Npgsql/Npgsql/NpgsqlConnection.resx
  13. 397 0
      mcs/class/Npgsql/Npgsql/NpgsqlConnectionString.cs
  14. 118 0
      mcs/class/Npgsql/Npgsql/NpgsqlConnectionString.resx
  15. 440 75
      mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs
  16. 131 48
      mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs
  17. 130 24
      mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs
  18. 15 6
      mcs/class/Npgsql/Npgsql/NpgsqlError.cs
  19. 8 9
      mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs
  20. 113 129
      mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs
  21. 6 4
      mcs/class/Npgsql/Npgsql/NpgsqlParameterCollection.cs
  22. 22 6
      mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs
  23. 21 19
      mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs
  24. 1 1
      mcs/class/Npgsql/Npgsql/NpgsqlStartupState.cs
  25. 28 52
      mcs/class/Npgsql/Npgsql/NpgsqlState.cs
  26. 7 0
      mcs/class/Npgsql/Npgsql/NpgsqlState.resx
  27. 40 12
      mcs/class/Npgsql/Npgsql/NpgsqlTransaction.cs
  28. 3 0
      mcs/class/Npgsql/Npgsql/NpgsqlTransaction.resx
  29. 1 0
      mcs/class/Npgsql/Npgsql/PGUtil.cs
  30. 54 0
      mcs/class/Npgsql/NpgsqlTypes/NpgsqlDbType.cs
  31. 488 0
      mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs
  32. 210 0
      mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypes.cs
  33. 701 396
      mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs
  34. BIN
      mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.resources
  35. 499 45
      mcs/class/Npgsql/Test/CommandTests.cs
  36. 2 2
      mcs/class/Npgsql/Test/ConnectionTests.cs
  37. 9 8
      mcs/class/Npgsql/Test/DataAdapterTests.cs
  38. 49 16
      mcs/class/Npgsql/Test/DataReaderTests.cs

+ 4 - 0
mcs/class/Npgsql/Npgsql.dll.sources

@@ -11,6 +11,7 @@ Npgsql/NpgsqlCommand.cs
 Npgsql/NpgsqlCommandBuilder.cs
 Npgsql/NpgsqlConnectedState.cs
 Npgsql/NpgsqlConnection.cs
+Npgsql/NpgsqlConnectionString.cs
 Npgsql/NpgsqlConnector.cs
 Npgsql/NpgsqlConnectorPool.cs
 Npgsql/NpgsqlDataAdapter.cs
@@ -46,4 +47,7 @@ Npgsql/Design/NpgsqlParameterConverter.cs
 Npgsql/Design/NpgsqlParametersEditor.cs
 Npgsql/Design/NpgsqlSysDescriptionAttribute.cs
 NpgsqlTypes/NpgsqlTypesHelper.cs
+NpgsqlTypes/NpgsqlDbType.cs
+NpgsqlTypes/NpgsqlTypeConverters.cs
+NpgsqlTypes/NpgsqlTypes.cs
 

+ 12 - 12
mcs/class/Npgsql/Npgsql/Design/ConnectionStringEditorForm.cs

@@ -54,15 +54,15 @@ namespace Npgsql.Design {
 			resman = new System.Resources.ResourceManager(typeof(ConnectionStringEditorForm));
 
 			this.pgconn.ConnectionString = ConnectionString;
-			this.tb_password.Text = this.pgconn.Password;
-			this.tb_port.Text = this.pgconn.ServerPort.ToString();
-			this.tb_server.Text = this.pgconn.ServerName;
-			this.tb_username.Text = this.pgconn.UserName;
+			this.tb_server.Text = this.pgconn.Host;
+			this.tb_port.Text = this.pgconn.Port.ToString();
 			this.tb_timeout.Text = this.pgconn.ConnectionTimeout.ToString();
 			if (this.pgconn.Database != "") {
 				this.cb_select_db.Items.Add(this.pgconn.Database);
 				this.cb_select_db.SelectedIndex = 0;
 			}
+			this.tb_username.Text = this.pgconn.UserName;
+			this.tb_password.Text = this.pgconn.Password;
 		}
 
 		/// <summary>
@@ -727,8 +727,8 @@ namespace Npgsql.Design {
 					return false;
 				}
 				sw.Write("Server={0};", this.tb_server.Text);
-				if(this.tb_port.Text != String.Empty){
-					sw.Write("Port={0};", tb_port.Text);
+				if(this.tb_port.Text != String.Empty && Convert.ToInt32(this.tb_port.Text) != ConnectionStringDefaults.Port){
+					sw.Write("{0}={1};", ConnectionStringKeys.Port, tb_port.Text);
 				}
 				// this happens if the user clicks Ok or Check Connection
 				// before selecting a database
@@ -739,15 +739,14 @@ namespace Npgsql.Design {
 					// this happens if the user clicks the database-combobox
 					// in order to select a database
 				else if(fillComboBox == true && (String)this.cb_select_db.Text == String.Empty){
-					sw.Write("Database=template1;");
+					sw.Write("{0}=template1;", ConnectionStringKeys.Database);
 				}
 				else{
-					sw.Write("Database={0};", this.cb_select_db.Text);
+					sw.Write("{0}={1};", ConnectionStringKeys.Database, this.cb_select_db.Text);
 				}
 				try{
-					if(this.tb_timeout.Text == String.Empty){}
-					else if(Int32.Parse(this.tb_timeout.Text) > 0){
-						sw.Write("Connect Timeout={0};", this.tb_timeout.Text);
+					if(this.tb_timeout.Text != String.Empty && Convert.ToInt32(this.tb_timeout.Text) != ConnectionStringDefaults.Timeout){
+						sw.Write("{0}={1};", ConnectionStringKeys.Timeout, this.tb_timeout.Text);
 					}
 				}
 					// don't mind if the value is nonsense - just don't put it into the string
@@ -760,7 +759,8 @@ namespace Npgsql.Design {
 					return false;
 				}
 
-				sw.Write("User Id={0};Password={1};", this.tb_username.Text, this.tb_password.Text);
+				sw.Write("{0}={1};", ConnectionStringKeys.UserName, this.tb_username.Text);
+				sw.Write("{0}={1};", ConnectionStringKeys.Password, this.tb_password.Text);
 				this.pgconn.ConnectionString = sw.ToString();
 				this.pgconn.Open();
 				if(fillComboBox == true){

+ 60 - 14
mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs

@@ -28,8 +28,8 @@ using System.Collections;
 using System.IO;
 using System.Text;
 using System.Net;
-using NpgsqlTypes;
 
+using NpgsqlTypes;
 
 namespace Npgsql
 {
@@ -44,14 +44,11 @@ namespace Npgsql
         private static readonly String CLASSNAME = "NpgsqlAsciiRow";
 
         private readonly Int16        READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
-        private Hashtable             oid_to_name_mapping;
 
-        public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, Hashtable oidToNameMapping, ProtocolVersion protocolVersion)
+        public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion)
         : base(rowDesc, protocolVersion)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
-
-            oid_to_name_mapping = oidToNameMapping;
         }
 
         public override void ReadFromStream(Stream inputStream, Encoding encoding)
@@ -77,6 +74,14 @@ namespace Npgsql
 
             Array.Clear(null_map_array, 0, null_map_array.Length);
 
+            
+            // Decoders used to get decoded chars when using unicode like encodings which may have chars crossing the byte buffer bounds.
+            
+            Decoder decoder = encoding.GetDecoder();
+            Char[] chars = null;
+            Int32 charCount;
+            
+	    
             // Read the null fields bitmap.
             PGUtil.CheckedStreamRead(inputStream, null_map_array, 0, null_map_array.Length );
 
@@ -94,6 +99,7 @@ namespace Npgsql
 
                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, 4);
 
+                NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
                 Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
                 field_value_size -= 4;
                 Int32 bytes_left = field_value_size;
@@ -105,8 +111,13 @@ namespace Npgsql
                     // Now, read just the field value.
                     PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
 
-                    // Read the bytes as string.
-                    result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
+                    charCount = decoder.GetCharCount(input_buffer, 0, READ_BUFFER_SIZE);
+
+                    chars = new Char[charCount];
+                    
+                    decoder.GetChars(input_buffer, 0, READ_BUFFER_SIZE, chars, 0);
+
+                    result.Append(new String(chars));
 
                     bytes_left -= READ_BUFFER_SIZE;
                 }
@@ -114,12 +125,16 @@ namespace Npgsql
                 // Now, read just the field value.
                 PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
 
-                // Read the bytes as string.
-                result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
+                
+                charCount = decoder.GetCharCount(input_buffer, 0, bytes_left);
+                chars = new Char[charCount];
+                decoder.GetChars(input_buffer, 0, bytes_left, chars, 0);
+
+                result.Append(new String(chars));
 
 
                 // Add them to the AsciiRow data.
-                data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(oid_to_name_mapping, result.ToString(), row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
+                data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result.ToString(), field_descr.type_size, field_descr.type_modifier));
 
             }
         }
@@ -133,6 +148,10 @@ namespace Npgsql
             PGUtil.ReadInt32(inputStream, input_buffer);
             Int16 numCols = PGUtil.ReadInt16(inputStream, input_buffer);
 
+            Decoder decoder = encoding.GetDecoder();
+            Char[] chars = null;
+            Int32 charCount;
+
             for (Int16 field_count = 0; field_count < numCols; field_count++)
             {
                 Int32 field_value_size = PGUtil.ReadInt32(inputStream, input_buffer);
@@ -144,18 +163,35 @@ namespace Npgsql
                     continue;
                 }
 
+                NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
                 Int32           bytes_left = field_value_size;
                 StringBuilder   result = new StringBuilder();
 
                 while (bytes_left > READ_BUFFER_SIZE)
                 {
+		
                     // Now, read just the field value.
                     PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
 
                     // Read the bytes as string.
-                    result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
+                    //result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
+                    charCount = decoder.GetCharCount(input_buffer, 0, READ_BUFFER_SIZE);
+
+                    chars = new Char[charCount];
+                    
+                    decoder.GetChars(input_buffer, 0, READ_BUFFER_SIZE, chars, 0);
+
+                    result.Append(new String(chars));
 
                     bytes_left -= READ_BUFFER_SIZE;
+
+                    // Now, read just the field value.
+                    /*PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, READ_BUFFER_SIZE);
+
+                    // Read the bytes as string.
+                    result.Append(new String(encoding.GetChars(input_buffer, 0, READ_BUFFER_SIZE)));
+
+                    bytes_left -= READ_BUFFER_SIZE;*/
                 }
 
                 // Now, read just the field value.
@@ -164,12 +200,22 @@ namespace Npgsql
                 if (row_desc[field_count].format_code == FormatCode.Text)
                 {
                     // Read the bytes as string.
-                    result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
+                    //result.Append(new String(encoding.GetChars(input_buffer, 0, bytes_left)));
+
+
+                    charCount = decoder.GetCharCount(input_buffer, 0, bytes_left);
+                    chars = new Char[charCount];
+                    decoder.GetChars(input_buffer, 0, bytes_left, chars, 0);
+
+                    result.Append(new String(chars));
+
                     // Add them to the AsciiRow data.
-                    data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(oid_to_name_mapping, result.ToString(), row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
+                    data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result.ToString(), field_descr.type_size, field_descr.type_modifier));
+                    
                 }
                 else
-                    data.Add(NpgsqlTypesHelper.ConvertBackendBytesToStytemType(oid_to_name_mapping, input_buffer, encoding, field_value_size, row_desc[field_count].type_oid, row_desc[field_count].type_modifier));
+                    // FIXME: input_buffer isn't holding all the field value. This code isn't handling binary data correctly.
+                    data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, input_buffer, encoding, field_value_size, field_descr.type_modifier));
             }
         }
 

+ 1 - 1
mcs/class/Npgsql/Npgsql/NpgsqlBinaryRow.cs

@@ -28,8 +28,8 @@ using System.Collections;
 using System.IO;
 using System.Text;
 using System.Net;
-using NpgsqlTypes;
 
+using NpgsqlTypes;
 
 namespace Npgsql
 {

+ 40 - 26
mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs

@@ -25,8 +25,8 @@ using System;
 using System.IO;
 using System.Net;
 using System.Net.Sockets;
-using System.Resources;
 using System.Collections;
+
 using Mono.Security.Protocol.Tls;
 
 namespace Npgsql
@@ -54,14 +54,37 @@ namespace Npgsql
         }
 
 
-        public override void Open(NpgsqlConnection context)
+
+        /// <summary>
+        /// Resolve a host name or IP address.
+        /// This is needed because if you call Dns.Resolve() with an IP address, it will attempt
+        /// to resolve it as a host name, when it should just convert it to an IP address.
+        /// </summary>
+        /// <param name="HostName"></param>
+        private static IPAddress ResolveIPHost(String HostName)
+        {
+
+            try
+            {
+                // Is it a raw IP address?
+                return IPAddress.Parse(HostName);
+            }
+            catch (FormatException)
+            {
+                // Not an IP, must be a host name...
+                return Dns.Resolve(HostName).AddressList[0];
+            }
+        }
+
+        public override void Open(NpgsqlConnector context)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
-            
-            TcpClient tcpc = new TcpClient(context.ServerName, context.ServerPort);
+
+            TcpClient tcpc = new TcpClient();
+            tcpc.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
             Stream stream = tcpc.GetStream();
-            
-            // If the PostgreSQL server has SSL connections enabled Open SslClientStream if (response == 'S') {
+
+            // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
             if (context.SSL)
             {
                 PGUtil.WriteInt32(stream, 8);
@@ -70,32 +93,23 @@ namespace Npgsql
                 Char response = (Char)stream.ReadByte();
                 if (response == 'S')
                 {
-                    stream = new SslClientStream(tcpc.GetStream(),
-                                                 context.ServerName,
-                                                 true,
-                                                 Mono.Security.Protocol.Tls.SecurityProtocolType.Default);
-                    /*stream = new SslClientStream(
-                        tcpc.GetStream(),
-                        context.ServerName,
-                        true,
-                        Tls.SecurityProtocolType.Tls|
-                        Tls.SecurityProtocolType.Ssl3);*/
-
-
+                    stream = new SslClientStream(
+                                 tcpc.GetStream(),
+                                 context.Host,
+                                 true,
+                                 Mono.Security.Protocol.Tls.SecurityProtocolType.Default
+                             );
+
+                    ((SslClientStream)stream).ClientCertSelectionDelegate = new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback);
                     ((SslClientStream)stream).ServerCertValidationDelegate = new CertificateValidationCallback(context.DefaultCertificateValidationCallback);
-                    ((SslClientStream)stream).ClientCertSelectionDelegate = context.CertificateSelectionCallback;
-                    ((SslClientStream)stream).PrivateKeyCertSelectionDelegate = context.PrivateKeySelectionCallback;
-
+                    ((SslClientStream)stream).PrivateKeyCertSelectionDelegate = new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback);
                 }
             }
 
+            context.Stream = stream;
 
-
-            context.Connector.Stream = stream;
-
-            NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.ServerName, context.ServerPort);
+            NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
             ChangeState(context, NpgsqlConnectedState.Instance);
-            context.Startup();
         }
 
     }

+ 103 - 65
mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs

@@ -23,28 +23,31 @@
 // License along with this library; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-
 using System;
 using System.Data;
-using System.Net;
-using System.Net.Sockets;
-using System.IO;
 using System.Text;
+using System.Resources;
 using System.ComponentModel;
 using System.Collections;
+
 using NpgsqlTypes;
 using Npgsql.Design;
 
 namespace Npgsql
 {
     /// <summary>
-    /// Represents a SQL statement or function (stored procedure) to execute against a PostgreSQL database. This class cannot be inherited.
+    /// Represents a SQL statement or function (stored procedure) to execute
+    /// against a PostgreSQL database. This class cannot be inherited.
     /// </summary>
     [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlCommand)), ToolboxItem(true)]
     public sealed class NpgsqlCommand : Component, IDbCommand
     {
+        // Logging related values
+        private static readonly String CLASSNAME = "NpgsqlCommand";
+        private static ResourceManager resman = new ResourceManager(typeof(NpgsqlCommand));
 
         private NpgsqlConnection            connection;
+        private NpgsqlConnector             connector;
         private NpgsqlTransaction           transaction;
         private String                      text;
         private Int32                       timeout;
@@ -57,10 +60,6 @@ namespace Npgsql
         private NpgsqlParse                 parse;
         private NpgsqlBind                  bind;
 
-        // Logging related values
-        private static readonly String CLASSNAME = "NpgsqlCommand";
-        private System.Resources.ResourceManager resman;
-
         // Constructors
 
         /// <summary>
@@ -89,26 +88,33 @@ namespace Npgsql
         /// <param name="transaction">The <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> in which the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> executes.</param>
         public NpgsqlCommand(String cmdText, NpgsqlConnection connection, NpgsqlTransaction transaction)
         {
-            resman = new System.Resources.ResourceManager(this.GetType());
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
 
             planName = String.Empty;
             text = cmdText;
             this.connection = connection;
+            if (this.connection != null)
+            	this.connector = connection.Connector;
+            
             parameters = new NpgsqlParameterCollection();
             timeout = 20;
             type = CommandType.Text;
             this.Transaction = transaction;
         }
 
-        /*
         /// <summary>
-        /// Finalizer for <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
+        /// Used to execute internal commands.
         /// </summary>
-        ~NpgsqlCommand ()
+        internal NpgsqlCommand(String cmdText, NpgsqlConnector connector)
         {
-            Dispose(false);
-        }*/
+            resman = new System.Resources.ResourceManager(this.GetType());
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
+
+            planName = String.Empty;
+            text = cmdText;
+            this.connector = connector;
+            type = CommandType.Text;
+        }
 
         // Public properties.
         /// <summary>
@@ -183,7 +189,7 @@ namespace Npgsql
 
             set
             {
-                connection = (NpgsqlConnection) value;
+                Connection = (NpgsqlConnection) value;
                 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Connection", value);
             }
         }
@@ -205,13 +211,26 @@ namespace Npgsql
             {
                 if (this.transaction != null && this.transaction.Connection == null)
                     this.transaction = null;
-                if (this.connection != null && this.connection.InTransaction == true)
+                if (this.connection != null && this.Connector.Transaction != null)
                     throw new InvalidOperationException(resman.GetString("Exception_SetConnectionInTransaction"));
                 this.connection = value;
+                if (this.connection != null)
+                	connector = this.connection.Connector;
+                
                 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Connection", value);
             }
         }
 
+        internal NpgsqlConnector Connector {
+            get
+            {
+                if (connector == null && this.connection != null)
+                    connector = this.connection.Connector;
+                    
+                return connector;
+            }
+        }
+
         IDataParameterCollection IDbCommand.Parameters {
             get
             {
@@ -326,12 +345,12 @@ namespace Npgsql
             ExecuteCommand();
 
             // If nothing is returned, just return -1.
-            if(connection.Mediator.CompletedResponses.Count == 0) {
+            if(Connector.Mediator.CompletedResponses.Count == 0) {
                 return -1;
             }
 
             // Check if the response is available.
-            String firstCompletedResponse = (String)connection.Mediator.CompletedResponses[0];
+            String firstCompletedResponse = (String)Connector.Mediator.CompletedResponses[0];
 
             if (firstCompletedResponse == null)
                 return -1;
@@ -414,7 +433,7 @@ namespace Npgsql
             ExecuteCommand();
 
             // Get the resultsets and create a Datareader with them.
-            return new NpgsqlDataReader(connection.Mediator.ResultSets, connection.Mediator.CompletedResponses, connection, cb);
+            return new NpgsqlDataReader(Connector.Mediator.ResultSets, Connector.Mediator.CompletedResponses, connection, cb);
         }
 
         ///<summary>
@@ -430,16 +449,16 @@ namespace Npgsql
                 for (Int32 i = 0; i < parameters.Count; i++)
                 {
                     // Do not quote strings, or escape existing quotes - this will be handled by the backend.
-                    parameterValues[i] = NpgsqlTypesHelper.ConvertNpgsqlParameterToBackendStringValue(parameters[i], false);
+                    parameterValues[i] = parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, true);
                 }
                 bind.ParameterValues = parameterValues;
             }
 
-            connection.Bind(bind);
-            connection.Mediator.RequireReadyForQuery = false;
-            connection.Flush();
+            Connector.Bind(bind);
+            Connector.Mediator.RequireReadyForQuery = false;
+            Connector.Flush();
 
-            connection.CheckErrorsAndNotifications();
+            connector.CheckErrorsAndNotifications();
         }
 
         /// <summary>
@@ -470,7 +489,7 @@ namespace Npgsql
             // Only the first column of the first row must be returned.
 
             // Get ResultSets.
-            ArrayList resultSets = connection.Mediator.ResultSets;
+            ArrayList resultSets = Connector.Mediator.ResultSets;
 
             // First data is the RowDescription object.
             // Check all resultsets as insert commands could have been sent along
@@ -501,13 +520,13 @@ namespace Npgsql
             // Check the connection state.
             CheckConnectionState();
 
-            if (! connection.SupportsPrepare) {
+            if (! Connector.SupportsPrepare) {
                 return;	// Do nothing.
             }
 
-            if (connection.BackendProtocolVersion == ProtocolVersion.Version2)
+            if (connector.BackendProtocolVersion == ProtocolVersion.Version2)
             {
-                NpgsqlCommand command = new NpgsqlCommand(GetPrepareCommandText(), connection );
+                NpgsqlCommand command = new NpgsqlCommand(GetPrepareCommandText(), connector );
                 command.ExecuteNonQuery();
             }
             else
@@ -518,12 +537,12 @@ namespace Npgsql
 
                 parse = new NpgsqlParse(planName, GetParseCommandText(), new Int32[] {});
 
-                connection.Parse(parse);
-                connection.Mediator.RequireReadyForQuery = false;
-                connection.Flush();
+                Connector.Parse(parse);
+                Connector.Mediator.RequireReadyForQuery = false;
+                Connector.Flush();
 
                 // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
+                connector.CheckErrorsAndNotifications();
 
                 bind = new NpgsqlBind(portalName, planName, new Int16[] {0}, null, new Int16[] {0});
             }
@@ -558,13 +577,11 @@ namespace Npgsql
         private void CheckConnectionState()
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckConnectionState");
-
+            
             // Check the connection state.
-            if (connection == null)
-                throw new InvalidOperationException(resman.GetString("Exception_ConnectionNull"));
-            if (connection.State != ConnectionState.Open)
+            if (Connector == null || Connector.State != ConnectionState.Open) {
                 throw new InvalidOperationException(resman.GetString("Exception_ConnectionNotOpen"));
-
+            }
         }
 
         /// <summary>
@@ -592,31 +609,34 @@ namespace Npgsql
             String result = text;
 
             if (type == CommandType.StoredProcedure)
-                if (connection.SupportsPrepare)
+                if (Connector.SupportsPrepare)
                     result = "select * from " + result; // This syntax is only available in 7.3+ as well SupportsPrepare.
                 else
                     result = "select " + result;				// Only a single result return supported. 7.2 and earlier.
             else if (type == CommandType.TableDirect)
                 return "select * from " + result; // There is no parameter support on table direct.
 
-            if (parameters.Count == 0)
+            if (parameters == null || parameters.Count == 0)
                 return result;
 
 
             //CheckParameters();
 
-            String parameterName;
-
             for (Int32 i = 0; i < parameters.Count; i++)
             {
-                parameterName = parameters[i].ParameterName;
-
-                result = ReplaceParameterValue(result, parameterName, NpgsqlTypesHelper.ConvertNpgsqlParameterToBackendStringValue(parameters[i], true));
-
+                NpgsqlParameter Param = parameters[i];
+                
+                // FIXME DEBUG ONLY
+                // adding the '::<datatype>' on the end of a parameter is a highly
+                // questionable practice, but it is great for debugging!
+                result = ReplaceParameterValue(
+                            result,
+                            Param.ParameterName,
+                            Param.TypeInfo.ConvertToBackend(Param.Value, false) + "::" + Param.TypeInfo.Name
+                         );
             }
 
             return result;
-
         }
 
 
@@ -634,7 +654,7 @@ namespace Npgsql
 
             for (Int32 i = 0; i < parameters.Count; i++)
             {
-                result.Append(NpgsqlTypesHelper.ConvertNpgsqlParameterToBackendStringValue(parameters[i], false) + ',');
+                result.Append(parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, false) + ',');
             }
 
             result = result.Remove(result.Length - 1, 1);
@@ -722,7 +742,8 @@ namespace Npgsql
 
                 for (i = 0; i < parameters.Count; i++)
                 {
-                    command.Append(NpgsqlTypesHelper.GetBackendTypeNameFromDbType(parameters[i].DbType));
+//                    command.Append(NpgsqlTypesHelper.GetDefaultTypeInfo(parameters[i].DbType));
+                    command.Append(parameters[i].TypeInfo.Name);
 
                     command.Append(',');
                 }
@@ -755,7 +776,9 @@ namespace Npgsql
                         (result[paramEnd] == ' ' ||
                          result[paramEnd] == ',' ||
                          result[paramEnd] == ')' ||
-                         result[paramEnd] == ';'))
+                         result[paramEnd] == ';' ||
+                         result[paramEnd] == '\n' ||
+                         result[paramEnd] == '\t'))
                 {
                     result = result.Substring(0, paramStart) + paramVal + result.Substring(paramEnd);
                     found = true;
@@ -783,28 +806,43 @@ namespace Npgsql
         {
             // Check the connection state first.
             CheckConnectionState();
+	    
+            // reset any responses just before getting new ones
+            connector.Mediator.ResetResponses();
 
-            if (parse == null) {
-                connection.Query(this);
 
-                // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
-            } else {
-                BindParameters();
+            if (parse == null) {
+                Connector.Query(this);
 
                 // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
+                connector.CheckErrorsAndNotifications();
+            } 
+            else 
+            {
+                try
+                {
+					
+                    BindParameters();
 
-                connection.Execute(new NpgsqlExecute(bind.PortalName, 0));
+                    // Check for errors and/or notifications and do the Right Thing.
+                    connector.CheckErrorsAndNotifications();
 
-                // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
-            }
-            /*	else
-            		throw new NotImplementedException(resman.GetString("Exception_CommandTypeTableDirect"));*/
-        }
+                    connector.Execute(new NpgsqlExecute(bind.PortalName, 0));
 
+                    // Check for errors and/or notifications and do the Right Thing.
+                    connector.CheckErrorsAndNotifications();
+                }
+                finally
+                {
+                    // As per documentation:
+                    // "[...] When an error is detected while processing any extended-query message,
+                    // the backend issues ErrorResponse, then reads and discards messages until a
+                    // Sync is reached, then issues ReadyForQuery and returns to normal message processing.[...]"
+                    // So, send a sync command if we get any problems.
 
+                    connector.Sync();
+                }
+            }           
+        }
     }
-
 }

+ 2 - 2
mcs/class/Npgsql/Npgsql/NpgsqlConnectedState.cs

@@ -48,11 +48,11 @@ namespace Npgsql
             }
         }
 
-        public override void Startup(NpgsqlConnection context)
+        public override void Startup(NpgsqlConnector context)
         {
             NpgsqlStartupPacket startupPacket  = new NpgsqlStartupPacket(296, //Not used.
                                                   context.BackendProtocolVersion,
-                                                  context.DatabaseName,
+                                                  context.Database,
                                                   context.UserName,
                                                   "",
                                                   "",

+ 255 - 645
mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs

@@ -26,21 +26,25 @@
 using System;
 using System.ComponentModel;
 using System.Data;
-using System.Net;
-using System.Net.Sockets;
-using System.IO;
 using System.Text;
 using System.Collections;
-using System.Collections.Specialized;
-using System.Threading;
+using System.Resources;
+using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
+
 using Mono.Security.Protocol.Tls;
+
 using NpgsqlTypes;
 using Npgsql.Design;
 
-
 namespace Npgsql
 {
+    /// <summary>
+    /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notice</see> events.
+    /// </summary>
+    /// <param name="e">A <see cref="Npgsql.NpgsqlNoticeEventArgs">NpgsqlNoticeEventArgs</see> that contains the event data.</param>
+    public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e);
+
     /// <summary>
     /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notification</see> events.
     /// </summary>
@@ -53,87 +57,51 @@ namespace Npgsql
     /// PostgreSQL server.
     /// </summary>
     [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))]
-    public sealed class NpgsqlConnection : Component, IDbConnection
+    public sealed class NpgsqlConnection : Component, IDbConnection, ICloneable
     {
         // Logging related values
-        private readonly String CLASSNAME = "NpgsqlConnection";
+        private static readonly String CLASSNAME = "NpgsqlConnection";
+        private static ResourceManager resman = new System.Resources.ResourceManager(typeof(NpgsqlConnection));
 
         /// <summary>
-        /// Occurs on NotificationResponses from the PostgreSQL backend.
+        /// Occurs on NoticeResponses from the PostgreSQL backend.
         /// </summary>
-        public event NotificationEventHandler   Notification;
+        public event NoticeEventHandler			           Notice;
+        internal NoticeEventHandler                    NoticeDelegate;
 
         /// <summary>
-        /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
+        /// Occurs on NotificationResponses from the PostgreSQL backend.
         /// </summary>
-        public CertificateValidationCallback    CertificateValidationCallback;
+        public event NotificationEventHandler          Notification;
+        internal NotificationEventHandler              NotificationDelegate;
 
         /// <summary>
         /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate.
         /// </summary>
-        public CertificateSelectionCallback     CertificateSelectionCallback;
+        public event CertificateSelectionCallback      CertificateSelectionCallback;
+        internal CertificateSelectionCallback          CertificateSelectionCallbackDelegate;
+
+        /// <summary>
+        /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
+        /// </summary>
+        public event CertificateValidationCallback     CertificateValidationCallback;
+        internal CertificateValidationCallback         CertificateValidationCallbackDelegate;
 
         /// <summary>
         /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate.
         /// </summary>
-        public PrivateKeySelectionCallback      PrivateKeySelectionCallback;
-
-        private NpgsqlState			                state;
-        private bool                            disposed = false;
-
-        private ConnectionState                 connection_state;
-//        private String                          connection_string;
-        private ListDictionary                  connection_string_values;
-        // some of the following constants are needed
-        // for designtime support so I made them 'internal'
-        // as I didn't want to add another interface for internal access
-        // --brar
-        // In the connection string
-        internal static readonly Char CONN_DELIM	= ';';  // Delimeter
-        internal static readonly Char CONN_ASSIGN	= '=';
-        internal static readonly String CONN_SERVER 	= "SERVER";
-        internal static readonly String CONN_PORT 	= "PORT";
-        internal static readonly String CONN_PROTOCOL 	= "PROTOCOL";
-        internal static readonly String CONN_DATABASE	= "DATABASE";
-        internal static readonly String CONN_USERID 	= "USER ID";
-        internal static readonly String CONN_PASSWORD	= "PASSWORD";
-        internal static readonly String CONN_SSL_ENABLED	= "SSL";
-        internal static readonly String CONN_ENCODING = "ENCODING";
-        internal static readonly String CONN_TIMEOUT = "TIMEOUT";
-
-        // These are for ODBC connection string compatibility
-        internal static readonly String ODBC_USERID 	= "UID";
-        internal static readonly String ODBC_PASSWORD = "PWD";
-
-        // These are for the connection pool
-        internal static readonly String POOLING       = "POOLING";
-        internal static readonly String MIN_POOL_SIZE = "MINPOOLSIZE";
-        internal static readonly String MAX_POOL_SIZE = "MAXPOOLSIZE";
-
-        // Connection string defaults
-        internal static readonly Int32 DEF_PORT = 5432;
-        internal static readonly String DEF_ENCODING = "SQL_ASCII";
-        internal static readonly Int32 DEF_MIN_POOL_SIZE = 1;
-        internal static readonly Int32 DEF_MAX_POOL_SIZE = 20;
-        internal static readonly Int32 DEF_TIMEOUT = 15;
-
-
-        // Values for possible CancelRequest messages.
-        private NpgsqlBackEndKeyData            backend_keydata;
-
-        // Flag for transaction status.
-        private Boolean                         _inTransaction = false;
-
-        // Mediator which will hold data generated from backend.
-        private NpgsqlMediator                  _mediator;
-        // Connector being used for the active connection.
-        private Connector                       _connector;
+        public event PrivateKeySelectionCallback       PrivateKeySelectionCallback;
+        internal PrivateKeySelectionCallback           PrivateKeySelectionCallbackDelegate;
 
-        private Boolean                         _supportsPrepare = false;
+        // Set this when disposed is called.
+        private bool                                   disposed = false;
 
-        private Hashtable                       _oidToNameMapping;
+        // Connection string values.
+        private NpgsqlConnectionString                 connection_string;
+
+        // Connector being used for the active connection.
+        private NpgsqlConnector                        connector = null;
 
-        private System.Resources.ResourceManager resman;
 
         /// <summary>
         /// Initializes a new instance of the
@@ -150,17 +118,17 @@ namespace Npgsql
         /// <param name="ConnectionString">The connection used to open the PostgreSQL database.</param>
         public NpgsqlConnection(String ConnectionString)
         {
-            resman = new System.Resources.ResourceManager(this.GetType());
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, ConnectionString);
 
-            connection_state = ConnectionState.Closed;
-            state = NpgsqlClosedState.Instance;
+            connection_string = NpgsqlConnectionString.ParseConnectionString(ConnectionString);
+            LogConnectionString();
 
-            _mediator = new NpgsqlMediator();
-            _connector = null;
-            _oidToNameMapping = new Hashtable();
+            NoticeDelegate = new NoticeEventHandler(OnNotice);
+            NotificationDelegate = new NotificationEventHandler(OnNotification);
 
-            connection_string_values = ParseConnectionString(ConnectionString);
+            CertificateValidationCallbackDelegate = new CertificateValidationCallback(DefaultCertificateValidationCallback);
+            CertificateSelectionCallbackDelegate = new CertificateSelectionCallback(DefaultCertificateSelectionCallback);
+            PrivateKeySelectionCallbackDelegate = new PrivateKeySelectionCallback(DefaultPrivateKeySelectionCallback);
         }
 
         /// <summary>
@@ -172,9 +140,10 @@ namespace Npgsql
         /// Database:      Database name. Defaults to user name if not specified;
         /// User:          User name;
         /// Password:      Password for clear text authentication;
-        /// Pooling:       True or False. Controls whether connection pooling is used.  Default = True;
+        /// SSL:           True or False. Controls whether to attempt a secure connection. Default = False;
+        /// Pooling:       True or False. Controls whether connection pooling is used. Default = True;
         /// MinPoolSize:   Min size of connection pool;
-        // (NOT USED AT THIS TIME) MaxPoolSize:   Max size of connection pool;
+        /// MaxPoolSize:   Max size of connection pool;
         /// Encoding:      Encoding to be used;
         /// Timeout:       Time to wait for connection open in seconds.
         /// </summary>
@@ -188,29 +157,49 @@ namespace Npgsql
         public String ConnectionString {
             get
             {
-                StringBuilder      S = new StringBuilder();
-
-                foreach (DictionaryEntry DE in connection_string_values) {
-                    S.AppendFormat("{0}={1};", DE.Key, DE.Value);
-                }
-
-                return S.ToString();
-//                return connection_string;
+                return connection_string.ToString();
             }
             set
             {
+                // Connection string is used as the key to the connector.  Because of this,
+                // we cannot change it while we own a connector.
+                CheckConnectionClosed();
                 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value);
-                connection_string_values = ParseConnectionString(value);
+                connection_string = NpgsqlConnectionString.ParseConnectionString(value);
+                LogConnectionString();
+            }
+        }
+
+        /// <summary>
+        /// Backend server host name.
+        /// </summary>
+        [Browsable(true)]
+        public String Host {
+            get
+            {
+                return connection_string.ToString(ConnectionStringKeys.Host);
+            }
+        }
+
+        /// <summary>
+        /// Backend server port.
+        /// </summary>
+        [Browsable(true)]
+        public Int32 Port {
+            get
+            {
+                return connection_string.ToInt32(ConnectionStringKeys.Port, ConnectionStringDefaults.Port);
             }
         }
 
         /// <summary>
-        /// Gets the ListDictionary containing the parsed connection string values.
+        /// If true, the connection will attempt to use SSL.
         /// </summary>
-        internal ListDictionary ConnectionStringValues {
+        [Browsable(true)]
+        public Boolean SSL {
             get
             {
-                return connection_string_values;
+                return connection_string.ToBool(ConnectionStringKeys.SSL);
             }
         }
 
@@ -223,7 +212,7 @@ namespace Npgsql
         public Int32 ConnectionTimeout {
             get
             {
-                return this.ConnectStringValueToInt32(CONN_TIMEOUT, DEF_TIMEOUT);
+                return connection_string.ToInt32(ConnectionStringKeys.Timeout, ConnectionStringDefaults.Timeout);
             }
         }
 
@@ -231,12 +220,12 @@ namespace Npgsql
         /// Gets the name of the current database or the database to be used after a connection is opened.
         /// </summary>
         /// <value>The name of the current database or the name of the database to be
-        /// used after a connection is opened. The default value is an empty string.</value>
+        /// used after a connection is opened. The default value is the empty string.</value>
         [NpgsqlSysDescription("Description_Database", typeof(NpgsqlConnection))]
         public String Database {
             get
             {
-                return DatabaseName;
+                return connection_string.ToString(ConnectionStringKeys.Database);
             }
         }
 
@@ -248,7 +237,39 @@ namespace Npgsql
         public ConnectionState State {
             get
             {
-                return connection_state;
+                CheckNotDisposed();
+
+                if (connector != null) {
+                    return connector.State;
+                } else {
+                    return ConnectionState.Closed;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Version of the PostgreSQL backend.
+        /// This can only be called when there is an active connection.
+        /// </summary>
+        [Browsable(false)]
+        public ServerVersion ServerVersion {
+            get
+            {
+                CheckConnectionOpen();
+                return connector.ServerVersion;
+            }
+        }
+
+        /// <summary>
+        /// Protocol version in use.
+        /// This can only be called when there is an active connection.
+        /// </summary>
+        [Browsable(false)]
+        public ProtocolVersion BackendProtocolVersion {
+            get
+            {
+                CheckConnectionOpen();
+                return connector.BackendProtocolVersion;
             }
         }
 
@@ -312,13 +333,38 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction", level);
 
-            if (_inTransaction)
-                throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions"));
+            CheckConnectionOpen();
 
+            if (connector.Transaction != null) {
+                throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions"));
+            }
 
             return new NpgsqlTransaction(this, level);
         }
 
+        /// <summary>
+        /// Opens a database connection with the property settings specified by the
+        /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
+        /// </summary>
+        public void Open()
+        {
+            CheckConnectionClosed();
+
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
+
+            // Check if there is any missing argument.
+            if (! connection_string.Contains(ConnectionStringKeys.Host))
+                throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.Host);
+            if (! connection_string.Contains(ConnectionStringKeys.UserName))
+                throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.UserName);
+
+            // Get a Connector.  The connector returned is guaranteed to be connected and ready to go.
+            connector = NpgsqlConnectorPool.ConnectorPoolMgr.RequestConnector (this);
+
+            connector.Notice += NoticeDelegate;
+            connector.Notification += NotificationDelegate;
+        }
+
         /// <summary>
         /// This method changes the current database by disconnecting from the actual
         /// database and connecting to the specified.
@@ -326,6 +372,8 @@ namespace Npgsql
         /// <param name="dbName">The name of the database to use in place of the current database.</param>
         public void ChangeDatabase(String dbName)
         {
+            CheckNotDisposed();
+
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeDatabase", dbName);
 
             if (dbName == null)
@@ -334,171 +382,34 @@ namespace Npgsql
             if (dbName == String.Empty)
                 throw new ArgumentOutOfRangeException(String.Format(resman.GetString("Exception_InvalidDbName"), dbName), "dbName");
 
-            if(this.connection_state != ConnectionState.Open)
-                throw new InvalidOperationException(resman.GetString("Exception_ChangeDatabaseOnOpenConn"));
+            String oldDatabaseName = Database;
 
-            String oldDatabaseName = ConnectStringValueToString(CONN_DATABASE);
             Close();
 
-            connection_string_values[CONN_DATABASE] = dbName;            
+            connection_string[ConnectionStringKeys.Database] = dbName;            
 
             Open();
         }
 
-        /// <summary>
-        /// Opens a database connection with the property settings specified by the
-        /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
-        /// </summary>
-        public void Open()
-        {
-            if (disposed) {
-                throw new ObjectDisposedException(CLASSNAME);
-            }
-
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
-
-            // Check if the connection is already open.
-            if (connection_state == ConnectionState.Open) {
-                throw new InvalidOperationException(resman.GetString("Exception_ConnOpen"));
-            }
-
-            bool                 ForcedProtocolVersion = false;
-            ProtocolVersion      PV;
-
-            // Check if there is any missing argument.
-            if (connection_string_values[CONN_SERVER] == null)
-                throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), CONN_SERVER);
-            if (connection_string_values[CONN_USERID] == null)
-                throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), CONN_USERID);
-
-            if (MaxPoolSize < 0)
-                throw new ArgumentOutOfRangeException("Numeric argument must not be less than zero.", MAX_POOL_SIZE);
-            if (Timeout < 0)
-                throw new ArgumentOutOfRangeException("Numeric argument must not be less than zero.", CONN_TIMEOUT);
-
-            // If ConnectionString specifies a protocol version, we will 
-            // not try to fall back to version 2 on failure.
-            if (connection_string_values.Contains(CONN_PROTOCOL)) {
-                PV = ConnectStringValueToProtocolVersion(CONN_PROTOCOL);
-                ForcedProtocolVersion = true;
-            } else {
-                PV = ProtocolVersion.Version3;
-            }
-
-            _connector = ConnectorPool.ConnectorPoolMgr.RequestConnector (this);
-
-            if (! _connector.IsInitialized)
-            {
-                _connector.Encoding = Encoding.Default;
-                _connector.BackendProtocolVersion = PV;
-
-                try {
-                    // Reset state to initialize new connector in pool.
-                    CurrentState = NpgsqlClosedState.Instance;
-
-                    CurrentState.Open(this);
-
-                    // Check for protocol not supported.  If we have been told what protocol to use,
-                    // we will not try this step.
-                    if (_mediator.Errors.Count > 0 && ! ForcedProtocolVersion)
-                    {
-                        // If we attempted protocol version 3, it may be possible to drop back to version 2.
-                        if (BackendProtocolVersion == ProtocolVersion.Version3) {
-                            NpgsqlError       Error0 = (NpgsqlError)_mediator.Errors[0];
-
-                            // If NpgsqlError.ReadFromStream_Ver_3() encounters a version 2 error,
-                            // it will set its own protocol version to version 2.  That way, we can tell
-                            // easily if the error was a FATAL: protocol error.
-                            if (Error0.BackendProtocolVersion == ProtocolVersion.Version2)
-                            {
-                                // Try using the 2.0 protocol.
-                                _mediator.ResetResponses();
-                                _connector.BackendProtocolVersion = ProtocolVersion.Version2;
-                                CurrentState = NpgsqlClosedState.Instance;
-                                CurrentState.Open(this);
-                            }
-                        }
-                    }
-
-                    // Check for errors and do the Right Thing.
-                    CheckErrors();
-
-                    // Change the state of connection to open.
-                    connection_state = ConnectionState.Open;
-
-                    String       ServerVersionString = String.Empty;
-
-                    backend_keydata = _mediator.BackendKeyData;
-
-                    // First try to determine backend server version using the newest method.
-                    try {
-                        ServerVersionString = ((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]).ParameterValue;
-                    } catch {}
-
-                    // Fall back to the old way, SELECT VERSION().
-                    // This should not happen for protocol version 3+.
-                    if (ServerVersionString.Length == 0)
-                    {
-                        NpgsqlCommand command = new NpgsqlCommand("select version();set DATESTYLE TO ISO;", this);
-                        ServerVersionString = PGUtil.ExtractServerVersion( (String)command.ExecuteScalar() );
-                    }
-
-                    // Cook version string so we can use it for enabling/disabling things based on
-                    // backend version.
-                    _connector.ServerVersion = PGUtil.ParseServerVersion(ServerVersionString);
-
-                    // Adjust client encoding.
-
-                    //NpgsqlCommand commandEncoding1 = new NpgsqlCommand("show client_encoding", this);
-                    //String clientEncoding1 = (String)commandEncoding1.ExecuteScalar();
-
-                    if (ConnectStringValueToString(CONN_ENCODING, DEF_ENCODING).ToUpper() == "UNICODE")
-                    {
-                        _connector.Encoding = Encoding.UTF8;
-                        NpgsqlCommand commandEncoding = new NpgsqlCommand("SET CLIENT_ENCODING TO UNICODE", this);
-                        commandEncoding.ExecuteNonQuery();
-                    }
-                }
-                catch {
-                    // Force this connector to close because
-                    // it is in an inconsistent state, so we can't just
-                    // release it back to the pool.
-                    ConnectorPool.ConnectorPoolMgr.ReleaseConnector(_connector, true);
-                    _connector = null;
-
-                    CurrentState = NpgsqlClosedState.Instance;
-                    throw;
-                }
-
-                // The connector is now fully initialized. Beyond this point, it is
-                // safe to release it back to the pool.
-                _connector.IsInitialized = true;
-            }
-
-            connection_state = ConnectionState.Open;
-            CurrentState = NpgsqlReadyState.Instance;
-
-            ProcessServerVersion();
-            _oidToNameMapping = NpgsqlTypesHelper.LoadTypesMapping(this);
-        }
-
         /// <summary>
         /// Releases the connection to the database.  If the connection is pooled, it will be
         ///	made available for re-use.  If it is non-pooled, the actual connection will be shutdown.
         /// </summary>
         public void Close()
         {
-            if (_connector == null) {
+            CheckNotDisposed();
+
+            if (connector == null) {
                 return;
             }
 
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");
 
-            ConnectorPool.ConnectorPoolMgr.ReleaseConnector(_connector, false);
-            _connector = null;
+            connector.Notification -= NotificationDelegate;
+            connector.Notice -= NoticeDelegate;
 
-            connection_state = ConnectionState.Closed;
-            CurrentState = NpgsqlClosedState.Instance;
+            NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector);
+            connector = null;
         }
 
         /// <summary>
@@ -519,6 +430,8 @@ namespace Npgsql
         /// <returns>A <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> object.</returns>
         public NpgsqlCommand CreateCommand()
         {
+            CheckNotDisposed();
+
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateCommand");
             return new NpgsqlCommand("", this);
         }
@@ -533,265 +446,72 @@ namespace Npgsql
         {
             Close();
             base.Dispose (disposing);
-						disposed = true;
+            disposed = true;
         }
 
-
         /// <summary>
-        /// Create a new (unconnected) connection based on this one.
+        /// Create a new connection based on this one.
         /// </summary>
-        /// <returns>A new NpgsqlConnction object.</returns>
-        public Object Clone()
+        /// <returns>A new NpgsqlConnection object.</returns>
+        Object ICloneable.Clone()
         {
-            return new NpgsqlConnection(ConnectionString);
-        }
-
-
-        //         
-        // Private util methods
-        //
-
-        /// <summary>
-        /// This method parses a connection string.
-        /// It translates it to a list of key-value pairs.
-        /// </summary>
-        private ListDictionary ParseConnectionString(String CS)
-        {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ParseConnectionString");
-
-            ListDictionary new_values = new ListDictionary(CaseInsensitiveComparer.Default);
-            String[] pairs;
-            String[] keyvalue;
-
-            // Get the key-value pairs delimited by CONN_DELIM
-            pairs = CS.Split(new Char[] {CONN_DELIM});
-
-            // Now, for each pair, get its key-value.
-            foreach(String sraw in pairs)
-            {
-                String s = sraw.Trim();
-                String Key = "", Value = "";
-
-                // This happens when there are trailing/empty CONN_DELIMs
-                // Just ignore them.
-                if (s == "") {
-                    continue;
-                }
-
-                // Split this chunk on the first CONN_ASSIGN only.
-                keyvalue = s.Split(new Char[] {CONN_ASSIGN}, 2);
-
-                // Keys always get trimmed and uppercased.
-                Key = keyvalue[0].Trim().ToUpper();
-
-                // Make sure the key is even there...
-                if (Key.Length == 0) {
-                    throw new ArgumentException(resman.GetString("Exception_WrongKeyVal"), "<BLANK>");
-                }
-
-                // We don't expect keys this long, and it might be about to be put
-                // in an error message, so makes sure it is a sane length.
-                if (Key.Length > 20) {
-                    Key = Key.Substring(0, 20);
-                }
-
-                // Check if there is a key-value pair.
-                if (keyvalue.Length != 2) {
-                    throw new ArgumentException(resman.GetString("Exception_WrongKeyVal"), Key);
-                }
-
-                // Values always get trimmed.
-                Value = keyvalue[1].Trim();
-
-                // Do some ODBC related substitions
-                if (Key == ODBC_USERID) {
-                    Key = CONN_USERID;
-                } else if (Key == ODBC_PASSWORD) {
-                    Key = CONN_PASSWORD;
-                }
-
-                NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, Key, Value);
-
-                // Add the pair to the dictionary..
-                new_values.Add(Key, Value);
-            }
-
-            return new_values;
+            return Clone();
         }
 
         /// <summary>
-        /// This method is required to set all the version dependent features flags.
-        /// SupportsPrepare means the server can use prepared query plans (7.3+)
+        /// Create a new connection based on this one.
         /// </summary>
-        private void ProcessServerVersion ()
+        /// <returns>A new NpgsqlConnection object.</returns>
+        public NpgsqlConnection Clone()
         {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessServerVersion");
+            CheckNotDisposed();
 
-            SupportsPrepare = ServerVersion >= new ServerVersion(7, 3, 0);
-        }
+            NpgsqlConnection C = new NpgsqlConnection(ConnectionString);
+            
+            C.Notice += this.Notice;
 
-        /// <summary>
-        /// The network stream connected to the backend.
-        /// This can only be called when there is an active connection.
-        /// </summary>
-        internal Stream Stream {
-            get
-            {
-                return _connector.Stream;
+            if (connector != null) {
+                C.Open();
             }
-        }
 
-        /// <summary>
-        /// The connector object connected to the backend.
-        /// </summary>
-        internal Connector Connector
-        {
-            get
-            {
-                return _connector;
-            }
+            return C;
         }
 
-        /// <summary>
-        /// Check for mediator errors (sent by backend) and throw the appropriate
-        /// exception if errors found.  This needs to be called after every interaction
-        /// with the backend.
-        /// </summary>
-        internal void CheckErrors()
+        //
+        // Internal methods and properties
+        //
+        internal void OnNotice(object O, NpgsqlNoticeEventArgs E)
         {
-            if (_mediator.Errors.Count > 0) {
-                throw new NpgsqlException(_mediator.Errors);
+            if (Notice != null) {
+                Notice(this, E);
             }
         }
 
-        /// <summary>
-        /// Check for notifications and fire the appropiate events.
-        /// This needs to be called after every interaction
-        /// with the backend.
-        /// </summary>
-        internal void CheckNotifications()
+        internal void OnNotification(object O, NpgsqlNotificationEventArgs E)
         {
             if (Notification != null) {
-                foreach (NpgsqlNotificationEventArgs E in _mediator.Notifications) {
-                    Notification(this, E);
-                }
+                Notification(this, E);
             }
         }
 
         /// <summary>
-        /// Check for errors AND notifications in one call.
-        /// </summary>
-        internal void CheckErrorsAndNotifications()
-        {
-            CheckErrors();
-            CheckNotifications();
-        }
-
-        // State
-        internal void Query (NpgsqlCommand queryCommand)
-        {
-            CurrentState.Query(this, queryCommand );
-        }
-
-        internal void Authenticate (string password)
-        {
-            CurrentState.Authenticate(this, password );
-        }
-
-        internal void Startup ()
-        {
-            CurrentState.Startup(this);
-        }
-
-        internal void Parse (NpgsqlParse parse)
-        {
-            CurrentState.Parse(this, parse);
-        }
-
-        internal void Flush ()
-        {
-            CurrentState.Flush(this);
-        }
-
-        internal void Sync ()
-        {
-            CurrentState.Sync(this);
-        }
-
-        internal void Bind (NpgsqlBind bind)
-        {
-            CurrentState.Bind(this, bind);
-        }
-
-        internal void Execute (NpgsqlExecute execute)
-        {
-            CurrentState.Execute(this, execute);
-        }
-
-
-        /// <summary>
-        /// Default SSL CertificateValidationCallback implementation.
+        /// The connector object connected to the backend.
         /// </summary>
-        internal bool DefaultCertificateValidationCallback(
-            X509Certificate       certificate,
-            int[]                 certificateErrors)
+        internal NpgsqlConnector Connector
         {
-            if (CertificateValidationCallback != null) {
-                return CertificateValidationCallback(certificate, certificateErrors);
-            } else {
-                return true;
-            }
-        }
-
-        internal NpgsqlState CurrentState {
-            get
-            {
-                return state;
-            }
-            set
-            {
-                state = value;
-            }
-        }
-
-        internal NpgsqlBackEndKeyData BackEndKeyData {
-            get
-            {
-                return backend_keydata;
-            }
-            set
-            {
-                backend_keydata = value;
-            }
-        }
-
-        /// <summary>
-        /// Backend server host name.
-        /// </summary>
-        internal String ServerName {
             get
             {
-                return ConnectStringValueToString(CONN_SERVER);
+                return connector;
             }
         }
 
         /// <summary>
-        /// Backend server port.
+        /// Gets the NpgsqlConnectionString containing the parsed connection string values.
         /// </summary>
-        internal Int32 ServerPort {
+        internal NpgsqlConnectionString ConnectionStringValues {
             get
             {
-                return ConnectStringValueToInt32(CONN_PORT, DEF_PORT);
-            }
-        }
-
-        /// <summary>
-        /// Backend database name.
-        /// </summary>
-        internal String DatabaseName {
-            get
-            {
-                return ConnectStringValueToString(CONN_DATABASE, UserName);
+                return connection_string;
             }
         }
 
@@ -801,7 +521,7 @@ namespace Npgsql
         internal String UserName {
             get
             {
-                return ConnectStringValueToString(CONN_USERID);
+                return connection_string.ToString(ConnectionStringKeys.UserName);
             }
         }
 
@@ -811,252 +531,142 @@ namespace Npgsql
         internal String Password {
             get
             {
-                return ConnectStringValueToString(CONN_PASSWORD);
+                return connection_string.ToString(ConnectionStringKeys.Password);
             }
         }
 
         /// <summary>
-        /// If true, the connection will attempt to use SSL.
+        /// Determine if connection pooling will be used for this connection.
         /// </summary>
-        internal Boolean SSL {
+        internal Boolean Pooling {
             get
             {
-                return ConnectStringValueToBool(CONN_SSL_ENABLED);
+                return (
+                    connection_string.ToBool(ConnectionStringKeys.Pooling, ConnectionStringDefaults.Pooling) &&
+                    connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, ConnectionStringDefaults.MaxPoolSize) > 0
+                );
             }
         }
 
-        /// <summary>
-        /// Client encoding currently in use.
-        /// This can only be called when there is an active connection.
-        /// </summary>
-        internal Encoding Encoding {
+        internal Int32 MinPoolSize {
             get
             {
-                return _connector.Encoding;
+                return connection_string.ToInt32(ConnectionStringKeys.MinPoolSize, 0, MaxPoolSize, ConnectionStringDefaults.MinPoolSize);
             }
         }
 
-        /// <summary>
-        /// The connection mediator.
-        /// </summary>
-        internal NpgsqlMediator	Mediator {
+        internal Int32 MaxPoolSize {
             get
             {
-                return _mediator;
+                return connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, 0, 1024, ConnectionStringDefaults.MaxPoolSize);
             }
         }
 
-        /// <summary>
-        /// Report if the connection is in a transaction.
-        /// </summary>
-        internal Boolean InTransaction {
+        internal Int32 Timeout {
             get
             {
-                return _inTransaction;
-            }
-            set
-            {
-                _inTransaction = value;
+                return connection_string.ToInt32(ConnectionStringKeys.Timeout, 0, 1024, ConnectionStringDefaults.Timeout);
             }
         }
 
+
+
+        //
+        // Event handlers
+        //
+
         /// <summary>
-        /// Report whether the current connection can support prepare functionality.
+        /// Default SSL CertificateSelectionCallback implementation.
         /// </summary>
-        internal Boolean SupportsPrepare {
-            get
-            {
-                return _supportsPrepare;
-            }
-            set
-            {
-                _supportsPrepare = value;
+        internal X509Certificate DefaultCertificateSelectionCallback(
+            X509CertificateCollection      clientCertificates,
+            X509Certificate                serverCertificate,
+            string                         targetHost,
+            X509CertificateCollection      serverRequestedCertificates)
+        {
+            if (CertificateSelectionCallback != null) {
+                return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
+            } else {
+                return null;
             }
         }
 
         /// <summary>
-        /// Version of the PostgreSQL backend.
-        /// This can only be called when there is an active connection.
+        /// Default SSL CertificateValidationCallback implementation.
         /// </summary>
-        [Browsable(false)]
-        public ServerVersion ServerVersion {
-            get
-            {
-                if (_connector == null) {
-                    throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen"));
-                }
-                return _connector.ServerVersion;
+        internal bool DefaultCertificateValidationCallback(
+            X509Certificate       certificate,
+            int[]                 certificateErrors)
+        {
+            if (CertificateValidationCallback != null) {
+                return CertificateValidationCallback(certificate, certificateErrors);
+            } else {
+                return true;
             }
         }
 
         /// <summary>
-        /// Protocol version in use.
-        /// This can only be called when there is an active connection.
+        /// Default SSL PrivateKeySelectionCallback implementation.
         /// </summary>
-        [Browsable(false)]
-        public ProtocolVersion BackendProtocolVersion {
-            get
-            {
-                if (_connector == null) {
-                    throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen"));
-                }
-                return _connector.BackendProtocolVersion;
-            }
-        }
-
-        internal Hashtable OidToNameMapping {
-            get
-            {
-                return _oidToNameMapping;
-            }
-            set
-            {
-                _oidToNameMapping = value;
-            }
-
-        }
-
-        internal Boolean Pooling {
-            get
-            {
-                return this.ConnectStringValueToBool(POOLING, true);
-            }
-        }
-
-        internal Int32 MinPoolSize {
-            get
-            {
-                return ConnectStringValueToInt32(MIN_POOL_SIZE, DEF_MIN_POOL_SIZE);
+        internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(
+            X509Certificate                certificate,
+            string                         targetHost)
+        {
+            if (PrivateKeySelectionCallback != null) {
+                return PrivateKeySelectionCallback(certificate, targetHost);
+            } else {
+                return null;
             }
         }
 
-        internal Int32 MaxPoolSize {
-            get
-            {
-                return ConnectStringValueToInt32(MAX_POOL_SIZE, DEF_MAX_POOL_SIZE);
-            }
-        }
 
-        internal Int32 Timeout {
-            get
-            {
-                return ConnectStringValueToInt32(CONN_TIMEOUT, DEF_TIMEOUT);
-            }
-        }
 
-        /// <summary>
-        /// Return a string value from the current connection string, even if the
-        /// given key is not in the string or if the value is null.
-        /// </summary>
-        internal String ConnectStringValueToString(String Key)
-        {
-            return ConnectStringValueToString(Key, "");
-        }
+        //
+        // Private methods and properties
+        //
+       
 
         /// <summary>
-        /// Return a string value from the current connection string, even if the
-        /// given key is not in the string or if the value is null.
+        /// Write each key/value pair in the connection string to the log.
         /// </summary>
-        internal String ConnectStringValueToString(String Key, String Default)
+        private void LogConnectionString()
         {
-            if (! connection_string_values.Contains(Key)) {
-                return Default;
+            foreach (DictionaryEntry DE in connection_string) {
+                NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, DE.Key, DE.Value);
             }
-
-            return Convert.ToString(connection_string_values[Key]);
         }
 
-        /// <summary>
-        /// Return an integer value from the current connection string, even if the
-        /// given key is not in the string or if the value is null.
-        /// Throw an appropriate exception if the value cannot be coerced to an integer.
-        /// </summary>
-        internal Int32 ConnectStringValueToInt32(String Key)
+        private void CheckConnectionOpen()
         {
-            return ConnectStringValueToInt32(Key, 0);
-        }
-
-        /// <summary>
-        /// Return an integer value from the current connection string, even if the
-        /// given key is not in the string.
-        /// Throw an appropriate exception if the value cannot be coerced to an integer.
-        /// </summary>
-        internal Int32 ConnectStringValueToInt32(String Key, Int32 Default)
-        {
-            if (! connection_string_values.Contains(Key)) {
-                return Default;
+            if (disposed) {
+                throw new ObjectDisposedException(CLASSNAME);
             }
 
-            try {
-                return Convert.ToInt32(connection_string_values[Key]);
-            } catch (Exception E) {
-                throw new ArgumentException(resman.GetString("Exception_InvalidIntegerKeyVal"), Key, E);
+            if (connector == null) {
+                throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen"));
             }
         }
 
-        /// <summary>
-        /// Return a boolean value from the current connection string, even if the
-        /// given key is not in the string.
-        /// Throw an appropriate exception if the value is not recognized as a boolean.
-        /// </summary>
-        internal Boolean ConnectStringValueToBool(String Key)
-        {
-            return ConnectStringValueToBool(Key, false);
-        }
-
-        /// <summary>
-        /// Return a boolean value from the current connection string, even if the
-        /// given key is not in the string.
-        /// Throw an appropriate exception if the value is not recognized as a boolean.
-        /// </summary>
-        internal Boolean ConnectStringValueToBool(String Key, Boolean Default)
+        private void CheckConnectionClosed()
         {
-            if (! connection_string_values.Contains(Key)) {
-                return Default;
+            if (disposed) {
+                throw new ObjectDisposedException(CLASSNAME);
             }
 
-            switch (connection_string_values[Key].ToString().ToLower()) {
-            case "t" :
-            case "true" :
-            case "y" :
-            case "yes" :
-                return true;
-
-            case "f" :
-            case "false" :
-            case "n" :
-            case "no" :
-                return false;
-
-            default :
-                throw new ArgumentException(resman.GetString("Exception_InvalidBooleanKeyVal"), Key);
-
+            if (connector != null) {
+                throw new InvalidOperationException(resman.GetString("Exception_ConnOpen"));
             }
         }
 
-        /// <summary>
-        /// Return a ProtocolVersion from the current connection string, even if the
-        /// given key is not in the string.
-        /// Throw an appropriate exception if the value is not recognized as
-        /// integer 2 or 3.
-        /// </summary>
-        private ProtocolVersion ConnectStringValueToProtocolVersion(String Key)
+        private void CheckNotDisposed()
         {
-            if (! connection_string_values.Contains(Key)) {
-                return ProtocolVersion.Version3;
+            if (disposed) {
+                throw new ObjectDisposedException(CLASSNAME);
             }
+        }
 
-            switch (ConnectStringValueToInt32(Key)) {
-            case 2 :
-                return ProtocolVersion.Version2;
+    }
 
-            case 3 :
-                return ProtocolVersion.Version3;
 
-            default :
-                throw new ArgumentException("Invalid protocol version specified in ConnectionString", Key);
 
-            }
-        }
-    }
 }

+ 0 - 3
mcs/class/Npgsql/Npgsql/NpgsqlConnection.de.resx

@@ -130,9 +130,6 @@
 	<data name="Exception_CloseError">
 		<value>Beim Aufruf von Close() sind Fehler aufgetreten</value>
 	</data>
-	<data name="Exception_WrongKeyVal">
-		<value>Schlüssel=Wert Argumentpaar im ConnectionString ist ungültig</value>
-	</data>
 	<data name="Log_ConnectionStringValues">
 		<value>ConnectionString Option: {0} = {1}</value>
 	</data>

+ 0 - 3
mcs/class/Npgsql/Npgsql/NpgsqlConnection.es.resx

@@ -130,9 +130,6 @@
 	<data name="Exception_CloseError">
 		<value>Error en Close()</value>
 	</data>
-	<data name="Exception_WrongKeyVal">
-		<value>key=value argumento incorrecto en ConnectionString</value>
-	</data>
 	<data name="Log_ConnectionStringValues">
 		<value>Opción de la cadena de conexión: {0} = {1}</value>
 	</data>

+ 0 - 3
mcs/class/Npgsql/Npgsql/NpgsqlConnection.fi.resx

@@ -130,9 +130,6 @@
 	<data name="Exception_CloseError">
 		<value>Virhe Close()-kutsussa</value>
 	</data>
-	<data name="Exception_WrongKeyVal">
-		<value>Virheellinen key=value -argumentti liittymistiedoissa</value>
-	</data>
 	<data name="Log_ConnectionStringValues">
 		<value>Liittymistietojen argumentti: {0} = {1}</value>
 	</data>

+ 3 - 18
mcs/class/Npgsql/Npgsql/NpgsqlConnection.resx

@@ -106,6 +106,9 @@
 	<data name="Description_Database">
 		<value>Current PostgreSQL database, 'Database=X' in the connection string</value>
 	</data>
+	<data name="Log_ConnectionStringValues">
+		<value>ConnectionString Option: {0} = {1}</value>
+	</data>
 	<data name="Exception_NoNestedTransactions">
 		<value>Nested/Concurrent transactions aren't supported.</value>
 	</data>
@@ -133,22 +136,4 @@
 	<data name="Exception_CloseError">
 		<value>Error in Close()</value>
 	</data>
-	<data name="Exception_WrongKeyVal">
-		<value>key=value argument incorrect in ConnectionString</value>
-	</data>
-	<data name="Exception_InvalidBooleanKeyVal">
-		<value>expecting key=[boolean] value in ConnectionString</value>
-	</data>
-	<data name="Exception_InvalidIntegerKeyVal">
-		<value>expecting key=[numeric] value in ConnectionString</value>
-	</data>
-	<data name="Log_ConnectionStringValues">
-		<value>Connection string option: {0} = {1}</value>
-	</data>
-	<data name="Exception_ChangeDatabaseOnOpenConn">
-		<value>The connection is not open. Use 'Database=X;' in the connection string to set the database for a closed connection.</value>
-	</data>
-    <data name="Exception_BackendErrors">
-		<value>There have been errors reported by the backend.</value>
-	</data>
 </root>

+ 397 - 0
mcs/class/Npgsql/Npgsql/NpgsqlConnectionString.cs

@@ -0,0 +1,397 @@
+// created on 6/21/2004
+
+// Npgsql.NpgsqlConnecionString.cs
+//
+// Author:
+//	Glen Parker ([email protected])
+//
+//	Copyright (C) 2002 The Npgsql Development Team
+//	[email protected]
+//	http://gborg.postgresql.org/project/npgsql/projdisplay.php
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Text;
+using System.Resources;
+
+namespace Npgsql
+{
+    /// <summary>
+    /// Represents a connection string.
+    /// </summary>
+    internal sealed class NpgsqlConnectionString : IEnumerable
+    {
+        // Logging related values
+        private static readonly String CLASSNAME = "NpgsqlConnectionString";
+        private static System.Resources.ResourceManager resman;
+
+        private String                                 connection_string = null;
+        private ListDictionary                         connection_string_values;
+
+        static NpgsqlConnectionString()
+        {
+            resman = new System.Resources.ResourceManager(typeof(NpgsqlConnectionString));
+        }
+
+        private NpgsqlConnectionString(NpgsqlConnectionString Other)
+        {
+            connection_string = Other.connection_string;
+            connection_string_values = new ListDictionary(CaseInsensitiveComparer.Default);
+            foreach (DictionaryEntry DE in Other.connection_string_values) {
+                connection_string_values.Add(DE.Key, DE.Value);
+            }
+        }
+
+        private NpgsqlConnectionString(ListDictionary Values)
+        {
+            connection_string_values = Values;
+        }
+
+        /// <summary>
+        /// Return an exact copy of this NpgsqlConnectionString.
+        /// </summary>
+        public NpgsqlConnectionString Clone()
+        {
+            return new NpgsqlConnectionString(this);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return connection_string_values.GetEnumerator();
+        }
+
+        /// <summary>
+        /// This method parses a connection string and returns a new NpgsqlConnectionString object.
+        /// </summary>
+        public static NpgsqlConnectionString ParseConnectionString(String CS)
+        {
+            ListDictionary new_values = new ListDictionary(CaseInsensitiveComparer.Default);
+            String[] pairs;
+            String[] keyvalue;
+
+            // Get the key-value pairs delimited by ;
+            pairs = CS.Split(';');
+
+            // Now, for each pair, get its key=value.
+            foreach(String sraw in pairs)
+            {
+                String s = sraw.Trim();
+                String Key = "", Value = "";
+
+                // Just ignore these.
+                if (s == "") {
+                    continue;
+                }
+
+                // Split this chunk on the first CONN_ASSIGN only.
+                keyvalue = s.Split(new Char[] {'='}, 2);
+
+                // Keys always get trimmed and uppercased.
+                Key = keyvalue[0].Trim().ToUpper();
+
+                // Make sure the key is even there...
+                if (Key.Length == 0) {
+                    throw new ArgumentException(resman.GetString("Exception_WrongKeyVal"), "<BLANK>");
+                }
+
+                // We don't expect keys this long, and it might be about to be put
+                // in an error message, so makes sure it is a sane length.
+                if (Key.Length > 20) {
+                    Key = Key.Substring(0, 20);
+                }
+
+                // Check if there is a key-value pair.
+                if (keyvalue.Length != 2) {
+                    throw new ArgumentException(resman.GetString("Exception_WrongKeyVal"), Key);
+                }
+
+                // Values always get trimmed.
+                Value = keyvalue[1].Trim();
+
+                // Substitute the real key name if this is an alias key (ODBC stuff for example)...
+                String      AliasKey = (string)ConnectionStringKeys.Aliases[Key];
+
+                if (AliasKey != null) {
+                    Key = AliasKey;
+                }
+
+                // Add the pair to the dictionary..
+                new_values.Add(Key, Value);
+            }
+
+            return new NpgsqlConnectionString(new_values);
+        }
+
+        /// <summary>
+        /// Case insensative accessor for indivual connection string values.
+        /// </summary>
+        public String this[String Key]
+        {
+            get
+            {
+                return (String)connection_string_values[Key];
+            }
+            set
+            {
+                connection_string_values[Key] = value;
+                connection_string = null;
+            }
+        }
+
+        /// <summary>
+        /// Report whether a value with the provided key name exists in this connection string.
+        /// </summary>
+        public Boolean Contains(String Key)
+        {
+            return connection_string_values.Contains(Key);
+        }
+
+        /// <summary>
+        /// Return a clean string representation of this connection string.
+        /// </summary>
+        public override String ToString()
+        {
+            if (connection_string == null) {
+                StringBuilder      S = new StringBuilder();
+
+                foreach (DictionaryEntry DE in this) {
+                    S.AppendFormat("{0}={1};", DE.Key, DE.Value);
+                }
+
+                connection_string = S.ToString();
+            }
+
+            return connection_string;
+        }
+
+        /// <summary>
+        /// Return a string value from the current connection string, even if the
+        /// given key is not in the string or if the value is null.
+        /// </summary>
+        public String ToString(String Key)
+        {
+            return ToString(Key, "");
+        }
+
+        /// <summary>
+        /// Return a string value from the current connection string, even if the
+        /// given key is not in the string or if the value is null.
+        /// </summary>
+        public String ToString(String Key, String Default)
+        {
+            if (! connection_string_values.Contains(Key)) {
+                return Default;
+            }
+
+            return Convert.ToString(connection_string_values[Key]);
+        }
+
+        /// <summary>
+        /// Return an integer value from the current connection string, even if the
+        /// given key is not in the string or if the value is null.
+        /// Throw an appropriate exception if the value cannot be coerced to an integer.
+        /// </summary>
+        public Int32 ToInt32(String Key)
+        {
+            return ToInt32(Key, 0);
+        }
+
+        /// <summary>
+        /// Return an integer value from the current connection string, even if the
+        /// given key is not in the string or if the value is null.
+        /// Throw an appropriate exception if the value cannot be coerced to an integer.
+        /// </summary>
+        public Int32 ToInt32(String Key, Int32 Min, Int32 Max)
+        {
+            return ToInt32(Key, Min, Max, 0);
+        }
+
+        /// <summary>
+        /// Return an integer value from the current connection string, even if the
+        /// given key is not in the string or if the value is null.
+        /// Throw an appropriate exception if the value cannot be coerced to an integer.
+        /// </summary>
+        public Int32 ToInt32(String Key, Int32 Default)
+        {
+            if (! connection_string_values.Contains(Key)) {
+                return Default;
+            }
+
+            try {
+                return Convert.ToInt32(connection_string_values[Key]);
+            } catch (Exception E) {
+                throw new ArgumentException(String.Format(resman.GetString("Exception_InvalidIntegerKeyVal"), Key), Key, E);
+            }
+        }
+
+        /// <summary>
+        /// Return an integer value from the current connection string, even if the
+        /// given key is not in the string.
+        /// Throw an appropriate exception if the value cannot be coerced to an integer.
+        /// </summary>
+        public Int32 ToInt32(String Key, Int32 Min, Int32 Max, Int32 Default)
+        {
+            Int32   V;
+
+            V = ToInt32(Key, Default);
+
+            if (V < Min) {
+                throw new ArgumentException(String.Format(resman.GetString("Exception_IntegerKeyValMin"), Key, Min), Key);
+            }
+            if (V > Max) {
+                throw new ArgumentException(String.Format(resman.GetString("Exception_IntegerKeyValMax"), Key, Max), Key);
+            }
+
+            return V;
+        }
+
+        /// <summary>
+        /// Return a boolean value from the current connection string, even if the
+        /// given key is not in the string.
+        /// Throw an appropriate exception if the value is not recognized as a boolean.
+        /// </summary>
+        public Boolean ToBool(String Key)
+        {
+            return ToBool(Key, false);
+        }
+
+        /// <summary>
+        /// Return a boolean value from the current connection string, even if the
+        /// given key is not in the string.
+        /// Throw an appropriate exception if the value is not recognized as a boolean.
+        /// </summary>
+        public Boolean ToBool(String Key, Boolean Default)
+        {
+            if (! connection_string_values.Contains(Key)) {
+                return Default;
+            }
+
+            switch (connection_string_values[Key].ToString().ToLower()) {
+            case "t" :
+            case "true" :
+            case "y" :
+            case "yes" :
+                return true;
+
+            case "f" :
+            case "false" :
+            case "n" :
+            case "no" :
+                return false;
+
+            default :
+                throw new ArgumentException(String.Format(resman.GetString("Exception_InvalidBooleanKeyVal"), Key), Key);
+
+            }
+        }
+
+        /// <summary>
+        /// Return a ProtocolVersion from the current connection string, even if the
+        /// given key is not in the string.
+        /// Throw an appropriate exception if the value is not recognized as
+        /// integer 2 or 3.
+        /// </summary>
+        public ProtocolVersion ToProtocolVersion(String Key)
+        {
+            if (! connection_string_values.Contains(Key)) {
+                return ProtocolVersion.Version3;
+            }
+
+            switch (ToInt32(Key)) {
+            case 2 :
+                return ProtocolVersion.Version2;
+
+            case 3 :
+                return ProtocolVersion.Version3;
+
+            default :
+                throw new ArgumentException(String.Format(resman.GetString("Exception_InvalidProtocolVersionKeyVal"), Key), Key);
+
+            }
+        }
+    }
+
+
+    /// <summary>
+    /// Know connection string keys.
+    /// </summary>
+    internal abstract class ConnectionStringKeys
+    {
+        public static readonly String Host             = "SERVER";
+        public static readonly String Port             = "PORT";
+        public static readonly String Protocol         = "PROTOCOL";
+        public static readonly String Database         = "DATABASE";
+        public static readonly String UserName         = "USER ID";
+        public static readonly String Password         = "PASSWORD";
+        public static readonly String SSL              = "SSL";
+        public static readonly String Encoding         = "ENCODING";
+        public static readonly String Timeout          = "TIMEOUT";
+
+        // These are for the connection pool
+        public static readonly String Pooling          = "POOLING";
+        public static readonly String MinPoolSize      = "MINPOOLSIZE";
+        public static readonly String MaxPoolSize      = "MAXPOOLSIZE";
+
+        // A list of aliases for some of the above values.  If one of these aliases is
+        // encountered when parsing a connection string, it's real key name will
+        // be used instead.  These will be reflected if ToString() is used to inspect
+        // the string.
+        private static ListDictionary _aliases;
+
+        static ConnectionStringKeys()
+        {
+            _aliases = new ListDictionary();
+
+            // Aliases to help catch common errors.
+            _aliases.Add("DB", Database);
+            _aliases.Add("HOST", Host);
+            _aliases.Add("USER", UserName);
+            _aliases.Add("USERID", UserName);
+            _aliases.Add("USER NAME", UserName);
+            _aliases.Add("USERNAME", UserName);
+            _aliases.Add("PSW", Password);
+
+            // Aliases to make migration from ODBC easier.
+            _aliases.Add("UID", UserName);
+            _aliases.Add("PWD", Password);
+        }
+
+        public static IDictionary Aliases
+        {
+            get
+            {
+                return _aliases;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Connection string default values.
+    /// </summary>
+    internal abstract class ConnectionStringDefaults
+    {
+        // Connection string defaults
+        public static readonly Int32 Port              = 5432;
+        public static readonly String Encoding         = "SQL_ASCII";
+        public static readonly Boolean Pooling         = true;
+        public static readonly Int32 MinPoolSize       = 1;
+        public static readonly Int32 MaxPoolSize       = 20;
+        public static readonly Int32 Timeout           = 15; // Seconds
+    }
+}

+ 118 - 0
mcs/class/Npgsql/Npgsql/NpgsqlConnectionString.resx

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<root>
+	<!-- 
+    Microsoft ResX Schema 
+    
+    Version 1.3
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">1.3</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1">this is my long string</data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        [base64 mime encoded serialized .NET Framework object]
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        [base64 mime encoded string representing a byte array form of the .NET Framework object]
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used forserialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+		<xsd:element name="root" msdata:IsDataSet="true">
+			<xsd:complexType>
+				<xsd:choice maxOccurs="unbounded">
+					<xsd:element name="data">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+							</xsd:sequence>
+							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="resheader">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+							</xsd:sequence>
+							<xsd:attribute name="name" type="xsd:string" use="required" />
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:choice>
+			</xsd:complexType>
+		</xsd:element>
+	</xsd:schema>
+	<resheader name="resmimetype">
+		<value>text/microsoft-resx</value>
+	</resheader>
+	<resheader name="version">
+		<value>1.3</value>
+	</resheader>
+	<resheader name="reader">
+		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+	</resheader>
+	<resheader name="writer">
+		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+	</resheader>
+	<data name="Exception_WrongKeyVal">
+		<value>key=value argument incorrect in ConnectionString</value>
+	</data>
+	<data name="Exception_InvalidBooleanKeyVal">
+		<value>expecting {0}=[True/False] value in ConnectionString</value>
+	</data>
+	<data name="Exception_InvalidIntegerKeyVal">
+		<value>expecting {0}=[Numeric] value in ConnectionString</value>
+	</data>
+	<data name="Exception_IntegerKeyValMax">
+		<value>numeric value {0} in ConnectionString exceeds maximum value {1}</value>
+	</data>
+	<data name="Exception_IntegerKeyValMin">
+		<value>numeric value {0} in ConnectionString is below minimum value {1}</value>
+	</data>
+	<data name="Exception_InvalidProtocolVersionKeyVal">
+		<value>expecting {0}=[Protocol Version] value in ConnectionString</value>
+	</data>
+</root>

+ 440 - 75
mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs

@@ -22,11 +22,20 @@
 //		Npgsql
 //	Status
 //		0.00.0000 - 06/17/2002 - ulrich sprick - created
+//		          - 06/??/2004 - Glen Parker<[email protected]> rewritten
 
 using System;
-using System.Net.Sockets;
+using System.Collections;
 using System.IO;
 using System.Text;
+using System.Data;
+using System.Security;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+using Mono.Security.Protocol.Tls;
+
+using NpgsqlTypes;
 
 namespace Npgsql
 {
@@ -36,45 +45,275 @@ namespace Npgsql
     /// access the physical connection to the database, and isolate
     /// the application developer from connection pooling internals.
     /// </summary>
-    internal class Connector
+    internal class NpgsqlConnector
     {
-        // Used to obtain a current key for the non-shared pool.
-        private NpgsqlConnection connection;
+        // Immutable.
+        internal NpgsqlConnectionString                ConnectionString;
+
+        /// <summary>
+        /// Occurs on NoticeResponses from the PostgreSQL backend.
+        /// </summary>
+        internal event NoticeEventHandler			         Notice;
 
-        private Stream _stream;
+        /// <summary>
+        /// Occurs on NotificationResponses from the PostgreSQL backend.
+        /// </summary>
+        internal event NotificationEventHandler        Notification;
 
-        private ProtocolVersion _backendProtocolVersion;
-        private ServerVersion _serverVersion;
+        /// <summary>
+        /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate.
+        /// </summary>
+        internal event CertificateSelectionCallback    CertificateSelectionCallback;
+
+        /// <summary>
+        /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
+        /// </summary>
+        internal event CertificateValidationCallback   CertificateValidationCallback;
+
+        /// <summary>
+        /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate.
+        /// </summary>
+        internal event PrivateKeySelectionCallback     PrivateKeySelectionCallback;
 
-        private Encoding _encoding;
+        private ConnectionState                  _connection_state;
+
+        // The physical network connection to the backend.
+        private Stream                           _stream;
+
+        // Mediator which will hold data generated from backend.
+        private NpgsqlMediator                   _mediator;
+
+        private ProtocolVersion                  _backendProtocolVersion;
+        private ServerVersion                    _serverVersion;
+
+        // Values for possible CancelRequest messages.
+        private NpgsqlBackEndKeyData             _backend_keydata;
+
+        // Flag for transaction status.
+//        private Boolean                         _inTransaction = false;
+        private NpgsqlTransaction                _transaction = null;
+
+        private Boolean                          _supportsPrepare = false;
+
+        private NpgsqlBackendTypeMapping         _oidToNameMapping = null;
+
+        private Encoding                         _encoding;
+
+        private Boolean                          _isInitialized;
+
+        private Boolean                          _pooled;
+        private Boolean                          _shared;
+
+        private NpgsqlState                      _state;
 
-        private Boolean _isInitialized;
 
-        private Boolean             _shared;
 
         /// <summary>
         /// Constructor.
         /// </summary>
         /// <param name="Shared">Controls whether the connector can be shared.</param>
-        public Connector(bool Shared)
+        public NpgsqlConnector(NpgsqlConnectionString ConnectionString, bool Pooled, bool Shared)
         {
+            this.ConnectionString = ConnectionString;
+            _connection_state = ConnectionState.Closed;
+            _pooled = Pooled;
             _shared = Shared;
             _isInitialized = false;
+            _state = NpgsqlClosedState.Instance;
+            _mediator = new NpgsqlMediator();
+            _oidToNameMapping = new NpgsqlBackendTypeMapping();
         }
 
-        /// <summary>
-        /// The NpgsqlConnection using this connector.  This will always return
-        /// null for shared connectors.
-        /// </summary>
-        internal NpgsqlConnection Connection
+
+        internal String Host
         {
             get
             {
-                return connection;
+                return ConnectionString.ToString(ConnectionStringKeys.Host);
             }
-            set
+        }
+
+        internal Int32 Port
+        {
+            get
+            {
+                return ConnectionString.ToInt32(ConnectionStringKeys.Port, ConnectionStringDefaults.Port);
+            }
+        }
+
+        internal String Database
+        {
+            get
+            {
+                return ConnectionString.ToString(ConnectionStringKeys.Database, UserName);
+            }
+        }
+
+        internal String UserName
+        {
+            get
+            {
+                return ConnectionString.ToString(ConnectionStringKeys.UserName);
+            }
+        }
+
+        internal String Password
+        {
+            get
+            {
+                return ConnectionString.ToString(ConnectionStringKeys.Password);
+            }
+        }
+
+        internal Boolean SSL
+        {
+            get
+            {
+                return ConnectionString.ToBool(ConnectionStringKeys.SSL);
+            }
+        }
+
+        /// <summary>
+        /// Gets the current state of the connection.
+        /// </summary>
+        internal ConnectionState State {
+            get
             {
-                connection = value;
+                return _connection_state;
+            }
+        }
+
+
+        // State
+        internal void Query (NpgsqlCommand queryCommand)
+        {
+            CurrentState.Query(this, queryCommand );
+        }
+
+        internal void Authenticate (string password)
+        {
+            CurrentState.Authenticate(this, password );
+        }
+
+        internal void Parse (NpgsqlParse parse)
+        {
+            CurrentState.Parse(this, parse);
+        }
+
+        internal void Flush ()
+        {
+            CurrentState.Flush(this);
+        }
+
+        internal void Sync ()
+        {
+            CurrentState.Sync(this);
+        }
+
+        internal void Bind (NpgsqlBind bind)
+        {
+            CurrentState.Bind(this, bind);
+        }
+
+        internal void Execute (NpgsqlExecute execute)
+        {
+            CurrentState.Execute(this, execute);
+        }
+
+
+
+
+        /// <summary>
+        /// Check for mediator errors (sent by backend) and throw the appropriate
+        /// exception if errors found.  This needs to be called after every interaction
+        /// with the backend.
+        /// </summary>
+        internal void CheckErrors()
+        {
+            if (_mediator.Errors.Count > 0) {
+                throw new NpgsqlException(_mediator.Errors);
+            }
+        }
+
+        /// <summary>
+        /// Check for notices and fire the appropiate events.
+        /// This needs to be called after every interaction
+        /// with the backend.
+        /// </summary>
+        internal void CheckNotices()
+        {
+            if (Notice != null) {
+                foreach (NpgsqlError E in _mediator.Notices) {
+                    Notice(this, new NpgsqlNoticeEventArgs(E));
+                }
+            }
+        }
+
+        /// <summary>
+        /// Check for notifications and fire the appropiate events.
+        /// This needs to be called after every interaction
+        /// with the backend.
+        /// </summary>
+        internal void CheckNotifications()
+        {
+            if (Notification != null) {
+                foreach (NpgsqlNotificationEventArgs E in _mediator.Notifications) {
+                    Notification(this, E);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Check for errors AND notifications in one call.
+        /// </summary>
+        internal void CheckErrorsAndNotifications()
+        {
+            CheckNotices();
+            CheckNotifications();
+            CheckErrors();
+        }
+
+        /// <summary>
+        /// Default SSL CertificateSelectionCallback implementation.
+        /// </summary>
+        internal X509Certificate DefaultCertificateSelectionCallback(
+            X509CertificateCollection      clientCertificates,
+            X509Certificate                serverCertificate,
+            string                         targetHost,
+            X509CertificateCollection      serverRequestedCertificates)
+        {
+            if (CertificateSelectionCallback != null) {
+                return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
+            } else {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// Default SSL CertificateValidationCallback implementation.
+        /// </summary>
+        internal bool DefaultCertificateValidationCallback(
+            X509Certificate       certificate,
+            int[]                 certificateErrors)
+        {
+            if (CertificateValidationCallback != null) {
+                return CertificateValidationCallback(certificate, certificateErrors);
+            } else {
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// Default SSL PrivateKeySelectionCallback implementation.
+        /// </summary>
+        internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(
+            X509Certificate                certificate,
+            string                         targetHost)
+        {
+            if (PrivateKeySelectionCallback != null) {
+                return PrivateKeySelectionCallback(certificate, targetHost);
+            } else {
+                return null;
             }
         }
 
@@ -149,10 +388,26 @@ namespace Npgsql
             }
         }
 
+        internal NpgsqlState CurrentState {
+            get
+            {
+                return _state;
+            }
+            set
+            {
+                _state = value;
+            }
+        }
+
+
+        internal bool Pooled
+        {
+            get
+            {
+                return _pooled;
+            }
+        }
 
-        /// <value>Reports whether this connector can be shared.</value>
-        /// <remarks>Set true if this connector is shared among multiple
-        /// connections.</remarks>
         internal bool Shared
         {
             get
@@ -161,10 +416,72 @@ namespace Npgsql
             }
         }
 
+        internal NpgsqlBackEndKeyData BackEndKeyData {
+            get
+            {
+                return _backend_keydata;
+            }
+        }
+
+        internal NpgsqlBackendTypeMapping OidToNameMapping {
+            get
+            {
+                return _oidToNameMapping;
+            }
+        }
+
+        /// <summary>
+        /// The connection mediator.
+        /// </summary>
+        internal NpgsqlMediator	Mediator {
+            get
+            {
+                return _mediator;
+            }
+        }
+
+        /// <summary>
+        /// Report if the connection is in a transaction.
+        /// </summary>
+        internal NpgsqlTransaction Transaction {
+            get
+            {
+                return _transaction;
+            }
+            set
+            {
+                _transaction = value;
+            }
+        }
+
+        /// <summary>
+        /// Report whether the current connection can support prepare functionality.
+        /// </summary>
+        internal Boolean SupportsPrepare {
+            get
+            {
+                return _supportsPrepare;
+            }
+            set
+            {
+                _supportsPrepare = value;
+            }
+        }
+
+        /// <summary>
+        /// This method is required to set all the version dependent features flags.
+        /// SupportsPrepare means the server can use prepared query plans (7.3+)
+        /// </summary>
+        // FIXME - should be private
+        internal void ProcessServerVersion ()
+        {
+            this._supportsPrepare = (ServerVersion >= new ServerVersion(7, 3, 0));
+        }
+
         /// <value>Counts the numbers of Connections that share
         /// this Connector. Used in Release() to decide wether this
         /// connector is to be moved to the PooledConnectors list.</value>
-        internal int mShareCount;
+        // internal int mShareCount;
 
         /// <summary>
         /// Opens the physical connection to the server.
@@ -173,9 +490,105 @@ namespace Npgsql
         /// Method of the connection pool manager.</remarks>
         internal void Open()
         {
-            //this.Socket = new Npgsql.Socket();
-            //this.Socket.Open(); // !!! to be fixed
-            //this.mOpen = true;
+            ProtocolVersion      PV;
+
+            // If Connection.ConnectionString specifies a protocol version, we will 
+            // not try to fall back to version 2 on failure.
+            if (ConnectionString.Contains(ConnectionStringKeys.Protocol)) {
+                PV = ConnectionString.ToProtocolVersion(ConnectionStringKeys.Protocol);
+            } else {
+                PV = ProtocolVersion.Unknown;
+            }
+
+            _backendProtocolVersion = (PV == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : PV;
+
+            // Reset state to initialize new connector in pool.
+            Encoding = Encoding.Default;
+            CurrentState = NpgsqlClosedState.Instance;
+
+            // Get a raw connection, possibly SSL...
+            CurrentState.Open(this);
+            // Establish protocol communication and handle authentication...
+            CurrentState.Startup(this);
+
+            // Check for protocol not supported.  If we have been told what protocol to use,
+            // we will not try this step.
+            if (_mediator.Errors.Count > 0 && PV == ProtocolVersion.Unknown)
+            {
+                // If we attempted protocol version 3, it may be possible to drop back to version 2.
+                if (BackendProtocolVersion == ProtocolVersion.Version3) {
+                    NpgsqlError       Error0 = (NpgsqlError)_mediator.Errors[0];
+
+                    // If NpgsqlError.ReadFromStream_Ver_3() encounters a version 2 error,
+                    // it will set its own protocol version to version 2.  That way, we can tell
+                    // easily if the error was a FATAL: protocol error.
+                    if (Error0.BackendProtocolVersion == ProtocolVersion.Version2)
+                    {
+                        // Try using the 2.0 protocol.
+                        _mediator.ResetResponses();
+                        BackendProtocolVersion = ProtocolVersion.Version2;
+                        CurrentState = NpgsqlClosedState.Instance;
+
+                        // Get a raw connection, possibly SSL...
+                        CurrentState.Open(this);
+                        // Establish protocol communication and handle authentication...
+                        CurrentState.Startup(this);
+                    }
+                }
+            }
+
+            // Check for errors and do the Right Thing.
+            // FIXME - CheckErrors needs to be moved to Connector
+            CheckErrors();
+
+            _backend_keydata = _mediator.BackendKeyData;
+
+            // Change the state of connection to open and ready.
+            _connection_state = ConnectionState.Open;
+            CurrentState = NpgsqlReadyState.Instance;
+
+            String       ServerVersionString = String.Empty;
+
+            // First try to determine backend server version using the newest method.
+            if (((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]) != null)
+                ServerVersionString = ((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]).ParameterValue; 
+            
+
+            // Fall back to the old way, SELECT VERSION().
+            // This should not happen for protocol version 3+.
+            if (ServerVersionString.Length == 0)
+            {
+                NpgsqlCommand command = new NpgsqlCommand("select version();set DATESTYLE TO ISO;", this);
+                ServerVersionString = PGUtil.ExtractServerVersion( (String)command.ExecuteScalar() );
+            }
+
+            // Cook version string so we can use it for enabling/disabling things based on
+            // backend version.
+            ServerVersion = PGUtil.ParseServerVersion(ServerVersionString);
+
+            // Adjust client encoding.
+
+            //NpgsqlCommand commandEncoding1 = new NpgsqlCommand("show client_encoding", _connector);
+            //String clientEncoding1 = (String)commandEncoding1.ExecuteScalar();
+
+            if (ConnectionString.ToString(ConnectionStringKeys.Encoding, ConnectionStringDefaults.Encoding).ToUpper() == "UNICODE")
+            {
+                Encoding = Encoding.UTF8;
+                NpgsqlCommand commandEncoding = new NpgsqlCommand("SET CLIENT_ENCODING TO UNICODE", this);
+                commandEncoding.ExecuteNonQuery();
+            }
+
+            // Make a shallow copy of the type mapping that the connector will own.
+            // It is possible that the connector may add types to its private
+            // mapping that will not be valid to another connector, even
+            // if connected to the same backend version.
+            _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone();
+
+            ProcessServerVersion();
+
+            // The connector is now fully initialized. Beyond this point, it is
+            // safe to release it back to the pool rather than closing it.
+            IsInitialized = true;
         }
 
 
@@ -184,57 +597,9 @@ namespace Npgsql
         /// </summary>
         internal void Close()
         {
-            // HACK HACK
-            // There needs to be a cleaner way to close this thing...
             try {
-                Stream.Close();
+                this.CurrentState.Close(this);
             } catch {}
         }
-
-        /*
-        /// <summary>
-        /// Releases a connector back to the pool manager's garding. Or to the
-        /// garbage collection.
-        /// </summary>
-        /// <remarks>The Shared and Pooled properties are no longer needed after
-        /// evaluation inside this method, so they are left in their current state.
-        ///	They get new meaning again when the connector is requested from the
-        /// pool manager later. </remarks>
-        public void Release()
-        {
-            if ( this.mShared )
-            {
-                // A shared connector is returned to the pooled connectors
-                // list only if it is not used by any Connection object.
-                // Otherwise the job is done by simply decrementing the
-                // usage counter:
-                if ( --this.mShareCount == 0 )
-                {
-                    Npgsql.ConnectorPool.ConnectorPoolMgr.CutOutConnector( this );
-                    // Shared connectors are *always* pooled after usage.
-                    // Depending on the Pooled property at this point
-                    // might introduce a lot of trouble into an application...
-                    Npgsql.ConnectorPool.ConnectorPoolMgr.InsertPooledConnector( this );
-                }
-            }
-            else // it is a nonshared connector
-            {
-                if ( this.Pooled )
-                {
-                    // Pooled connectors are simply put in the
-                    // PooledConnectors list for later recycling
-                    Npgsql.ConnectorPool.ConnectorPoolMgr.InsertPooledConnector( this );
-                }
-                else
-                {
-                    // Unpooled private connectors get the physical
-                    // connection closed, they are *not* recyled later.
-                    // Instead they are (implicitly) handed over to the
-                    // garbage collection.
-                    // !!! to be fixed
-                    //this.Socket.Close();
-                }
-            }
-        }*/
     }
 }

+ 131 - 48
mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs

@@ -25,12 +25,14 @@
 
 using System;
 using System.Collections;
-using Npgsql;
 using System.Threading;
 
 namespace Npgsql
 {
-    internal class ConnectorPool
+    /// <summary>
+    /// This class manages all connector objects, pooled AND non-pooled.
+    /// </summary>
+    internal class NpgsqlConnectorPool
     {
         /// <summary>
         /// A queue with an extra Int32 for keeping track of busy connections.
@@ -46,9 +48,9 @@ namespace Npgsql
 
         /// <value>Unique static instance of the connector pool
         /// mamager.</value>
-        internal static ConnectorPool ConnectorPoolMgr = new Npgsql.ConnectorPool();
+        internal static NpgsqlConnectorPool ConnectorPoolMgr = new NpgsqlConnectorPool();
 
-        public ConnectorPool()
+        public NpgsqlConnectorPool()
         {
             PooledConnectors = new Hashtable();
         }
@@ -75,22 +77,26 @@ namespace Npgsql
         /// the connector. Its ConnectionString will be used to search the
         /// pool for available connectors.</param>
         /// <returns>A connector object.</returns>
-        internal Npgsql.Connector RequestConnector (NpgsqlConnection Connection)
+        public NpgsqlConnector RequestConnector (NpgsqlConnection Connection)
         {
+            NpgsqlConnector     Connector;
+
             if (Connection.Pooling) {
-                return RequestPooledConnector(Connection);
+                Connector = RequestPooledConnector(Connection);
             } else {
-                return GetNonPooledConnector(Connection);
+                Connector = GetNonPooledConnector(Connection);
             }
+
+            return Connector;
         }
 
         /// <summary>
         /// Find a pooled connector.  Handle locking and timeout here.
         /// </summary>
-        private Npgsql.Connector RequestPooledConnector (NpgsqlConnection Connection)
+        private NpgsqlConnector RequestPooledConnector (NpgsqlConnection Connection)
         {
-            Connector       Connector;
-            Int32           timeoutMilliseconds = Connection.Timeout * 1000;
+            NpgsqlConnector     Connector;
+            Int32               timeoutMilliseconds = Connection.Timeout * 1000;
 
             lock(this)
             {
@@ -111,7 +117,11 @@ namespace Npgsql
             }
 
             if (Connector == null) {
-                throw new Exception("Timeout while getting a connection from pool.");
+                if (Connection.Timeout > 0) {
+                    throw new Exception("Timeout while getting a connection from pool.");
+                } else {
+                    throw new Exception("Connection pool exceeds maximum size.");
+                }
             }
 
             return Connector;
@@ -120,10 +130,10 @@ namespace Npgsql
         /// <summary>
         /// Find a pooled connector.  Handle shared/non-shared here.
         /// </summary>
-        private Npgsql.Connector RequestPooledConnectorInternal (NpgsqlConnection Connection)
+        private NpgsqlConnector RequestPooledConnectorInternal (NpgsqlConnection Connection)
         {
-            Connector       Connector = null;
-            Boolean         Shared = false;
+            NpgsqlConnector       Connector = null;
+            Boolean               Shared = false;
 
             // If sharing were implemented, I suppose Shared would be set based
             // on some property on the Connection.
@@ -148,33 +158,33 @@ namespace Npgsql
         /// </remarks>
         /// <param name="Connector">The connector to release.</param>
         /// <param name="ForceClose">Force the connector to close, even if it is pooled.</param>
-        public void ReleaseConnector (Connector Connector, bool ForceClose)
+        public void ReleaseConnector (NpgsqlConnection Connection, NpgsqlConnector Connector)
         {
-            if (Connector.Connection.Pooling) {
-                ReleasePooledConnector(Connector, ForceClose);
+            if (Connector.Pooled) {
+                ReleasePooledConnector(Connection, Connector);
             } else {
-                UngetNonPooledConnector(Connector);
+                UngetNonPooledConnector(Connection, Connector);
             }
         }
 
         /// <summary>
         /// Release a pooled connector.  Handle locking here.
         /// </summary>
-        private void ReleasePooledConnector (Connector Connector, bool ForceClose)
+        private void ReleasePooledConnector (NpgsqlConnection Connection, NpgsqlConnector Connector)
         {
             lock(this)
             {
-                ReleasePooledConnectorInternal(Connector, ForceClose);
+                ReleasePooledConnectorInternal(Connection, Connector);
             }
         }
 
         /// <summary>
         /// Release a pooled connector.  Handle shared/non-shared here.
         /// </summary>
-        private void ReleasePooledConnectorInternal (Connector Connector, bool ForceClose)
+        private void ReleasePooledConnectorInternal (NpgsqlConnection Connection, NpgsqlConnector Connector)
         {
             if (! Connector.Shared) {
-                UngetPooledConnector(Connector, ForceClose);
+                UngetPooledConnector(Connection, Connector);
             } else {
                 // Connection sharing? What's that?
                 throw new NotImplementedException("Internal: Shared pooling not implemented");
@@ -184,12 +194,17 @@ namespace Npgsql
         /// <summary>
         /// Create a connector without any pooling functionality.
         /// </summary>
-        private Npgsql.Connector GetNonPooledConnector(NpgsqlConnection Connection)
+        private NpgsqlConnector GetNonPooledConnector(NpgsqlConnection Connection)
         {
-            Connector       Connector;
+            NpgsqlConnector       Connector;
+
+            Connector = CreateConnector(Connection);
 
-            Connector = new Connector(false);
-            Connector.Connection = Connection;
+            Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate;
+            Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate;
+            Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate;
+
+            Connector.Open();
 
             return Connector;
         }
@@ -198,31 +213,65 @@ namespace Npgsql
         /// Find an available pooled connector in the non-shared pool, or create
         /// a new one if none found.
         /// </summary>
-        private Npgsql.Connector GetPooledConnector(NpgsqlConnection Connection)
+        private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection)
         {
-            ConnectorQueue  Queue;
-            Connector       Connector = null;
+            ConnectorQueue        Queue;
+            NpgsqlConnector       Connector = null;
 
             // Try to find a queue.
-            Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString];
+            Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()];
 
             if (Queue == null) {
                 Queue = new ConnectorQueue();
-                PooledConnectors[Connection.ConnectionString] = Queue;
+                PooledConnectors[Connection.ConnectionString.ToString()] = Queue;
             }
 
-			if (Queue.Count > 0) {
+            if (Queue.Count > 0) {
                 // Found a queue with connectors.  Grab the top one.
-                Connector = (Connector)Queue.Dequeue();
+                Connector = (NpgsqlConnector)Queue.Dequeue();
                 Queue.UseCount++;
-                Connector.Connection = Connection;
+
+                // We should do some sort of check here to see if this connector is still OK?
             } else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize) {
-                Connector = new Connector(false);
+                Connector = CreateConnector(Connection);
+
+                Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate;
+                Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate;
+                Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate;
+
+                try {
+                    Connector.Open();
+                } catch {
+                    try {
+                        Connector.Close();
+                    } catch {}
+
+                    throw;
+                }
+            
+
                 Queue.UseCount++;
-                Connector.Connection = Connection;
             }
-            
-            
+
+            // Meet the MinPoolSize requirement if needed.
+            if (Connection.MinPoolSize > 0) {
+                while (Queue.Count + Queue.UseCount < Connection.MinPoolSize) {
+                    NpgsqlConnector Spare = CreateConnector(Connection);
+
+                    Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate;
+                    Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate;
+                    Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate;
+
+                    Spare.Open();
+
+                    Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate;
+                    Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate;
+                    Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate;
+
+                    Queue.Enqueue(Connector);
+                }
+            }
+
             return Connector;
         }
 
@@ -230,52 +279,86 @@ namespace Npgsql
         /// Find an available shared connector in the shared pool, or create
         /// a new one if none found.
         /// </summary>
-        private Npgsql.Connector GetSharedConnector(NpgsqlConnection Connection)
+        private NpgsqlConnector GetSharedConnector(NpgsqlConnection Connection)
         {
             // To be implemented
 
             return null;
         }
 
+        private NpgsqlConnector CreateConnector(NpgsqlConnection Connection)
+        {
+            return new NpgsqlConnector(
+                Connection.ConnectionStringValues.Clone(),
+                Connection.Pooling,
+                false
+            );
+        }
+
         /// <summary>
         /// Close the connector.
         /// </summary>
         /// <param name="Connector">Connector to release</param>
-        private void UngetNonPooledConnector(Connector Connector)
+        private void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector)
         {
+            Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate;
+            Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate;
+            Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate;
+
+            if (Connector.Transaction != null) {
+                Connector.Transaction.Cancel();
+            }
+
             Connector.Close();
         }
 
         /// <summary>
-        /// Put a pooled connector into the pool queue.  Create the queue if needed.
+        /// Put a pooled connector into the pool queue.
         /// </summary>
         /// <param name="Connector">Connector to pool</param>
-        private void UngetPooledConnector(Connector Connector, bool ForceClose)
+        private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector)
         {
             ConnectorQueue           Queue;
 
             // Find the queue.
-            Queue = (ConnectorQueue)PooledConnectors[Connector.Connection.ConnectionString];
+            Queue = (ConnectorQueue)PooledConnectors[Connector.ConnectionString.ToString()];
 
             if (Queue == null) {
                 throw new InvalidOperationException("Internal: No connector queue found for existing connector.");
             }
 
-            if (ForceClose) {
+            Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate;
+            Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate;
+            Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate;
+
+            Queue.UseCount--;
+
+            if (! Connector.IsInitialized) {
+                if (Connector.Transaction != null) {
+                    Connector.Transaction.Cancel();
+                }
+
                 Connector.Close();
             } else {
-                Connector.Connection = null;
-                Queue.Enqueue(Connector);
+                if (Connector.Transaction != null) {
+                    try {
+                        Connector.Transaction.Rollback();
+                    } catch {
+                        Connector.Close();
+                    }
+                }
             }
 
-            Queue.UseCount--;
+            if (Connector.State == System.Data.ConnectionState.Open) {
+                Queue.Enqueue(Connector);
+            }
         }
 
         /// <summary>
         /// Stop sharing a shared connector.
         /// </summary>
         /// <param name="Connector">Connector to unshare</param>
-        private void UngetSharedConnector(Connector Connector)
+        private void UngetSharedConnector(NpgsqlConnection Connection, NpgsqlConnector Connector)
         {
             // To be implemented
         }

+ 130 - 24
mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs

@@ -26,15 +26,15 @@
 using System;
 using System.Data;
 using System.Collections;
-using NpgsqlTypes;
 
+using NpgsqlTypes;
 
 namespace Npgsql
 {
     /// <summary>
     /// Provides a means of reading a forward-only stream of rows from a PostgreSQL backend.  This class cannot be inherited.
     /// </summary>
-    public class NpgsqlDataReader : IDataReader, IEnumerable
+    public sealed class NpgsqlDataReader : IDataReader, IEnumerable
     {
         private NpgsqlConnection 	_connection;
         private ArrayList 			_resultsets;
@@ -77,7 +77,8 @@ namespace Npgsql
 
         private void CheckHaveResultSet()
         {
-            if (! HaveResultSet()) {
+            if (! HaveResultSet())
+            {
                 throw new InvalidOperationException("Cannot read data. No result set.");
             }
         }
@@ -86,9 +87,12 @@ namespace Npgsql
         {
             CheckHaveResultSet();
 
-            if (_rowIndex < 0) {
+            if (_rowIndex < 0)
+            {
                 throw new InvalidOperationException("DataReader positioned before beginning of result set. Did you call Read()?");
-            } else if (_rowIndex >= _currentResultset.Count) {
+            }
+            else if (_rowIndex >= _currentResultset.Count)
+            {
                 throw new InvalidOperationException("DataReader positioned beyond end of result set.");
             }
         }
@@ -147,21 +151,37 @@ namespace Npgsql
             {
                 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected");
 
-                if (HaveResultSet()) {
+                if (HaveResultSet())
+                {
                     return -1;
                 }
 
                 String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null);	// whitespace separator.
 
-                try {
+                try
+                {
                     return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]);
                 }
-                catch (FormatException) {
+                catch (FormatException)
+                {
                     return -1;
                 }
             }
         }
 
+        /// <summary>
+        /// Indicates if NpgsqlDatareader has rows to be read.
+        /// </summary>
+
+        public Boolean HasRows
+        {
+        	get
+        	{
+            	return _currentResultset.Count > 0;
+            }
+
+        }
+
         /// <summary>
         /// Closes the data reader object.
         /// </summary>
@@ -170,8 +190,10 @@ namespace Npgsql
             if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
             {
                 _connection.Close();
-                _isClosed = true;
+
             }
+
+            _isClosed = true;
         }
 
         /// <summary>
@@ -204,10 +226,13 @@ namespace Npgsql
 
             CheckHaveResultSet();
 
-            if (_rowIndex < _currentResultset.Count) {
+            if (_rowIndex < _currentResultset.Count)
+            {
                 _rowIndex++;
                 return (_rowIndex < _currentResultset.Count);
-            } else {
+            }
+            else
+            {
                 return false;
             }
         }
@@ -252,10 +277,23 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName");
 
-            if (! HaveResultSet())
-                return String.Empty;
-            else
-                return _currentResultset.RowDescription[Index].name;
+            CheckHaveResultSet();
+
+            return _currentResultset.RowDescription[Index].name;
+        }
+
+        /// <summary>
+        /// Return the data type OID of the column at index <param name="Index"></param>.
+        /// </summary>
+        public String GetDataTypeOID(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            return _currentResultset.RowDescription[Index].type_oid.ToString();
         }
 
         /// <summary>
@@ -263,11 +301,20 @@ namespace Npgsql
         /// </summary>
         public String GetDataTypeName(Int32 Index)
         {
-            // FIXME: have a type name instead of the oid
-            if (! HaveResultSet())
-                return String.Empty;
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return _currentResultset.RowDescription[Index].type_oid.ToString();
+            }
             else
-                return (_currentResultset.RowDescription[Index].type_oid).ToString();
+            {
+                return TI.Name;
+            }
         }
 
         /// <summary>
@@ -277,13 +324,65 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
 
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
 
-            if (! HaveResultSet())
-                return null;
+            if (TI == null)
+            {
+                return typeof(String);  //Default type is string.
+            }
             else
-                return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[Index].type_oid);
+            {
+                return TI.Type;
+            }
         }
 
+        /// <summary>
+        /// Return the data DbType of the column at index <param name="Index"></param>.
+        /// </summary>
+        public DbType GetFieldDbType(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return DbType.String;
+            }
+            else
+            {
+                //return TI.DBType;
+                return DbType.String;
+            }
+        }
+        
+        /// <summary>
+        /// Return the data NpgsqlDbType of the column at index <param name="Index"></param>.
+        /// </summary>
+        public NpgsqlDbType GetFieldNpgsqlDbType(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return NpgsqlDbType.Text;
+            }
+            else
+            {
+                return TI.NpgsqlDbType;
+                
+            }
+        }
+        
+
         /// <summary>
         /// Return the value of the column at index <param name="Index"></param>.
         /// </summary>
@@ -291,7 +390,8 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue");
 
-            if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields) {
+            if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields)
+            {
                 throw new IndexOutOfRangeException("Column index out of range");
             }
 
@@ -314,7 +414,8 @@ namespace Npgsql
             // It's also possible to pass an array with more that FieldCount elements.
             Int32 maxColumnIndex = (Values.Length < FieldCount) ? Values.Length : FieldCount;
 
-            for (Int32 i = 0; i < maxColumnIndex; i++) {
+            for (Int32 i = 0; i < maxColumnIndex; i++)
+            {
                 Values[i] = GetValue(i);
             }
 
@@ -524,6 +625,11 @@ namespace Npgsql
             return (GetValue(i) == DBNull.Value);
         }
 
+        internal NpgsqlBackendTypeInfo GetTypeInfo(Int32 FieldIndex)
+        {
+            return _currentResultset.RowDescription[FieldIndex].type_info;
+        }
+
         private DataTable GetResultsetSchema()
         {
 

+ 15 - 6
mcs/class/Npgsql/Npgsql/NpgsqlError.cs

@@ -29,12 +29,26 @@ using System.Text;
 
 namespace Npgsql
 {
+    /// <summary>
+    /// EventArgs class to send Notice parameters, which are just NpgsqlError's in a lighter context.
+    /// </summary>
+    public class NpgsqlNoticeEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Notice information.
+        /// </summary>
+        public NpgsqlError Notice = null;
+
+        internal NpgsqlNoticeEventArgs(NpgsqlError eNotice)
+        {
+            Notice = eNotice;
+        }
+    }
 
     /// <summary>
     /// This class represents the ErrorResponse and NoticeResponse
     /// message sent from PostgreSQL server.
     /// </summary>
-    ///
     public sealed class NpgsqlError
     {
         // Logging related values
@@ -186,14 +200,9 @@ namespace Npgsql
             return B.ToString();
         }
 
-        private NpgsqlError()
-        {}
-
-
         internal NpgsqlError(ProtocolVersion protocolVersion)
         {
             protocol_version = protocolVersion;
-
         }
 
         /// <summary>

+ 8 - 9
mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs

@@ -174,16 +174,15 @@ namespace Npgsql
             if (msglevel > level)
                 return;
 
-            if(Parameters.Length == 0)
-            {
-                string message = resman.GetString(ResourceString);
-                LogMsg(message, msglevel);
-            }
-            else
-            {
-                string message = String.Format(resman.GetString(ResourceString), Parameters);
-                LogMsg(message, msglevel);
+            string message = resman.GetString(ResourceString);
+
+            if (message == null) {
+                message = String.Format("Unable to find resource string {0} for class {1}", ResourceString, resman.BaseName);
+            } else if (Parameters.Length > 0) {
+                message = String.Format(message, Parameters);
             }
+
+            LogMsg(message, msglevel);
         }
 
         /// <summary>

+ 113 - 129
mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs

@@ -44,26 +44,22 @@ namespace Npgsql
         private static readonly String CLASSNAME = "NpgsqlParameter";
 
         // Fields to implement IDbDataParameter interface.
-        private byte 				precision = 0;
-        private byte 				scale = 0;
-        private Int32				size = 0;
+        private byte 				    precision = 0;
+        private byte 				    scale = 0;
+        private Int32				    size = 0;
 
         // Fields to implement IDataParameter
-        private DbType				db_type = DbType.String;
-        private ParameterDirection	direction = ParameterDirection.Input;
-        private Boolean				is_nullable = false;
-        private String				name;
-        private String				source_column = String.Empty;
-        private DataRowVersion		source_version = DataRowVersion.Current;
-        private Object				value;
+        //private NpgsqlDbType				    npgsqldb_type = NpgsqlDbType.Text;
+        //private DbType                    db_type = DbType.String;
+        private NpgsqlNativeTypeInfo	type_info;
+        private ParameterDirection	    direction = ParameterDirection.Input;
+        private Boolean				    is_nullable = false;
+        private String				    name;
+        private String				    source_column = String.Empty;
+        private DataRowVersion		    source_version = DataRowVersion.Current;
+        private Object				    value = DBNull.Value;
         private System.Resources.ResourceManager resman;
 
-
-
-
-
-#region Constructors
-
         /// <summary>
 
         /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> class.
@@ -84,116 +80,32 @@ namespace Npgsql
         /// <p>When you specify an <see cref="System.Object">Object</see>
         /// in the value parameter, the <see cref="System.Data.DbType">DbType</see> is
         /// inferred from the .NET Framework type of the <b>Object</b>.</p>
-        /// <p>Use caution when using this overload of the
-        /// <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> constructor
-        /// to specify integer parameter values. Because this overload takes a <i>value</i>
-        /// of type <b>Object</b>, you must convert the integral value to an <b>Object</b> type when
-        /// the value is zero, as the following C# example demonstrates.
-        /// <code>Parameter = new SqlParameter("@pname", Convert.ToInt32(0));</code>
-        /// If you do not perform this conversion, the compiler will assume you are
-        /// attempting to call the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> (string, SqlDbType) constructor overload.</p>
+        /// <p>When using this constructor, you must be aware of a possible misuse of the constructor which takes a DbType parameter.
+        /// This happens when calling this constructor passing an int 0 and the compiler thinks you are passing a value of DbType.
+        /// Use <code> Convert.ToInt32(value) </code> for example to have compiler calling the correct constructor.</p>
         /// </remarks>
         public NpgsqlParameter(String parameterName, object value)
         {
             resman = new System.Resources.ResourceManager(this.GetType());
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, parameterName, value);
 
-
-            this.value = value;
-
             this.ParameterName = parameterName;
+            this.value = value;
 
-
-            // Set db_type according to:
-            // http://msdn.microsoft.com/library/en-us/cpguide/html/cpconusingparameterswithdataadapters.asp
-            // Should this be in this.Value.set{}?
-            //I don't really know so I leave it here where it will not hurt.
-            if ((value == null) || (value == DBNull.Value) )
+            if ((this.value == null) || (this.value == DBNull.Value) )
             {
                 // don't really know what to do - leave default and do further exploration
+                // Default type for null values is String.
+                this.value = DBNull.Value;
+                type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String));
                 return;
+            } else {
+                type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value.GetType());
+                if (type_info == null) {
+    				throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value.GetType()));
+                }
+                
             }
-            Type type = value.GetType();
-            if (type == typeof(bool))
-            {
-                db_type = DbType.Boolean;
-            }
-            else if (type == typeof(byte))
-            {
-                db_type = DbType.Byte;
-            }
-            else if (type == typeof(byte[]))
-            {
-                db_type = DbType.Binary;
-            }
-            else if (type == typeof(char))
-            {
-                // There is no DbType.Char
-                db_type = DbType.String;
-            }
-            else if (type == typeof(DateTime))
-            {
-                db_type = DbType.DateTime;
-            }
-            else if (type == typeof(decimal))
-            {
-                db_type = DbType.Decimal;
-            }
-            else if (type == typeof(double))
-            {
-                db_type = DbType.Double;
-            }
-            else if (type == typeof(float))
-            {
-                db_type = DbType.Single;
-            }
-            else if (type == typeof(Guid))
-            {
-                db_type = DbType.Guid;
-            }
-            else if (type == typeof(Int16))
-            {
-                db_type = DbType.Int16;
-            }
-            else if (type == typeof(Int32))
-            {
-                db_type = DbType.Int32;
-            }
-            else if (type == typeof(Int64))
-            {
-                db_type = DbType.Int64;
-            }
-            else if (type == typeof(string))
-            {
-                db_type = DbType.String;
-            }
-            else if (type == typeof(TimeSpan))
-            {
-                db_type = DbType.Time;
-            }
-            else if (type == typeof(UInt16))
-            {
-                db_type = DbType.UInt16;
-            }
-            else if (type == typeof(UInt32))
-            {
-                db_type = DbType.UInt32;
-            }
-            else if (type == typeof(UInt64))
-            {
-                db_type = DbType.UInt64;
-            }
-            else if (type == typeof(object))
-            {
-                db_type = DbType.Object;
-            }
-            else
-            {
-                throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), type.ToString()));
-            }
-
-
-
         }
 
         /// <summary>
@@ -202,7 +114,11 @@ namespace Npgsql
         /// </summary>
         /// <param name="parameterName">The name of the parameter to map.</param>
         /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param>
-        public NpgsqlParameter(String parameterName, DbType parameterType) : this(parameterName, parameterType, 0, String.Empty)
+        public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType) : this(parameterName, parameterType, 0, String.Empty)
+        {}
+        
+        
+        public NpgsqlParameter(String parameterName, DbType parameterType) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, 0, String.Empty)
         {}
 
         /// <summary>
@@ -212,9 +128,13 @@ namespace Npgsql
         /// <param name="parameterName">The name of the parameter to map.</param>
         /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param>
         /// <param name="size">The length of the parameter.</param>
-        public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size) : this(parameterName, parameterType, size, String.Empty)
+        public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType, Int32 size) : this(parameterName, parameterType, size, String.Empty)
         {}
 
+        public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, String.Empty)
+        {}
+        
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>
         /// class with the parameter name, the <see cref="System.Data.DbType">DbType</see>, the size,
@@ -224,7 +144,7 @@ namespace Npgsql
         /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param>
         /// <param name="size">The length of the parameter.</param>
         /// <param name="sourceColumn">The name of the source column.</param>
-        public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size, String sourceColumn)
+        public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType, Int32 size, String sourceColumn)
         {
 
             resman = new System.Resources.ResourceManager(this.GetType());
@@ -232,10 +152,21 @@ namespace Npgsql
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, parameterName, parameterType, size, source_column);
 
             this.ParameterName = parameterName;
-            db_type = parameterType;
+            
+            type_info = NpgsqlTypesHelper.GetNativeTypeInfo(parameterType);
+            if (type_info == null)
+    			throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), parameterType));
+            
             this.size = size;
             source_column = sourceColumn;
+            
+            
         }
+        
+        public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size, String sourceColumn) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, sourceColumn)
+        {}
+        
+        
 
         /// <summary>
         /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>
@@ -258,14 +189,12 @@ namespace Npgsql
         /// <param name="sourceVersion">One of the <see cref="System.Data.DataRowVersion">DataRowVersion</see> values.</param>
         /// <param name="value">An <see cref="System.Object">Object</see> that is the value
         /// of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>.</param>
-        public NpgsqlParameter (String parameterName, DbType parameterType, Int32 size, String sourceColumn, ParameterDirection direction, bool isNullable, byte precision, byte scale, DataRowVersion sourceVersion, object value)
+        public NpgsqlParameter (String parameterName, NpgsqlDbType parameterType, Int32 size, String sourceColumn, ParameterDirection direction, bool isNullable, byte precision, byte scale, DataRowVersion sourceVersion, object value)
         {
 
             resman = new System.Resources.ResourceManager(this.GetType());
-
-
+ 
             this.ParameterName = parameterName;
-            this.DbType = parameterType;
             this.Size = size;
             this.SourceColumn = sourceColumn;
             this.Direction = direction;
@@ -274,9 +203,22 @@ namespace Npgsql
             this.Scale = scale;
             this.SourceVersion = sourceVersion;
             this.Value = value;
-        }
 
-#endregion
+            if (this.value == null) {
+                this.value = DBNull.Value;
+                type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String));
+            }
+            else
+            {
+                type_info = NpgsqlTypesHelper.GetNativeTypeInfo(parameterType);
+                if (type_info == null)
+                    throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), parameterType));
+            }
+            
+        }
+        
+        public NpgsqlParameter (String parameterName, DbType parameterType, Int32 size, String sourceColumn, ParameterDirection direction, bool isNullable, byte precision, byte scale, DataRowVersion sourceVersion, object value) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, sourceColumn, direction, isNullable, precision, scale, sourceVersion, value)
+        {}
 
         // Implementation of IDbDataParameter
         /// <summary>
@@ -356,14 +298,52 @@ namespace Npgsql
             get
             {
                 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "DbType");
-                return db_type;
+                return TypeInfo.DbType;
             }
 
             // [TODO] Validate data type.
             set
             {
                 NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "DbType", value);
-                db_type = value;
+                type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value);
+                if (type_info == null) 
+                    throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value));
+                
+            }
+        }
+        
+        /// <summary>
+        /// Gets or sets the <see cref="System.Data.DbType">DbType</see> of the parameter.
+        /// </summary>
+        /// <value>One of the <see cref="System.Data.DbType">DbType</see> values. The default is <b>String</b>.</value>
+        [Category("Data"), RefreshProperties(RefreshProperties.All), DefaultValue(NpgsqlDbType.Text)]
+        public NpgsqlDbType NpgsqlDbType
+        {
+            get
+            {
+                NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "DbType");
+                
+                return TypeInfo.NpgsqlDbType;
+            }
+
+            // [TODO] Validate data type.
+            set
+            {
+                NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "DbType", value);
+                type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value);
+                if (type_info == null)
+                    throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value));
+                
+            }
+        }
+
+        
+
+        internal NpgsqlNativeTypeInfo TypeInfo
+        {
+            get
+            {
+                return type_info;
             }
         }
 
@@ -426,8 +406,9 @@ namespace Npgsql
             set
             {
                 name = value;
-                if ( (name.Equals(String.Empty)) || (name[0] != ':') )
-                    name = ':' + name;
+                if ( (name.Equals(String.Empty)) || ((name[0] != ':') && (name[0] != '@')) )
+                     name = ':' + name;
+                
                 NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "ParameterName", value);
             }
         }
@@ -496,6 +477,9 @@ namespace Npgsql
             {
                 NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "Value", value);
                 this.value = value;
+                if (this.value == null) {
+                    this.value = DBNull.Value;
+                }
             }
         }
 
@@ -506,7 +490,7 @@ namespace Npgsql
         /// <returns>A new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> that is a copy of this instance.</returns>
         object System.ICloneable.Clone()
         {
-            return new NpgsqlParameter(this.ParameterName, this.DbType,	this.Size, this.SourceColumn, this.Direction, this.IsNullable, this.Precision, this.Scale, this.SourceVersion, this.Value);
+            return new NpgsqlParameter(this.ParameterName, this.NpgsqlDbType,	this.Size, this.SourceColumn, this.Direction, this.IsNullable, this.Precision, this.Scale, this.SourceVersion, this.Value);
         }
 
 

+ 6 - 4
mcs/class/Npgsql/Npgsql/NpgsqlParameterCollection.cs

@@ -32,6 +32,8 @@ using System.Data;
 using System.Collections;
 using System.ComponentModel;
 using Npgsql.Design;
+using NpgsqlTypes;
+
 
 namespace Npgsql
 {
@@ -152,7 +154,7 @@ namespace Npgsql
         /// <param name="parameterName">The name of the parameter.</param>
         /// <param name="parameterType">One of the DbType values.</param>
         /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns>
-        public NpgsqlParameter Add(string parameterName, DbType parameterType)
+        public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType);
             return this.Add(new NpgsqlParameter(parameterName, parameterType));
@@ -165,7 +167,7 @@ namespace Npgsql
         /// <param name="parameterType">One of the DbType values.</param>
         /// <param name="size">The length of the column.</param>
         /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns>
-        public NpgsqlParameter Add(string parameterName, DbType parameterType, int size)
+        public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size);
             return this.Add(new NpgsqlParameter(parameterName, parameterType, size));
@@ -179,7 +181,7 @@ namespace Npgsql
         /// <param name="size">The length of the column.</param>
         /// <param name="sourceColumn">The name of the source column.</param>
         /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns>
-        public NpgsqlParameter Add(string parameterName, DbType parameterType, int size, string sourceColumn)
+        public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size, string sourceColumn)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size, sourceColumn);
             return this.Add(new NpgsqlParameter(parameterName, parameterType, size, sourceColumn));
@@ -235,7 +237,7 @@ namespace Npgsql
 
             // Iterate values to see what is the index of parameter.
             Int32 index = 0;
-            if (parameterName[0] != ':')
+            if ( (parameterName[0] != ':') && (parameterName[0] != '@') )
                 parameterName = ':' + parameterName;
 
             foreach(NpgsqlParameter parameter in this)

+ 22 - 6
mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs

@@ -61,7 +61,7 @@ namespace Npgsql
 
 
 
-        public override void Query( NpgsqlConnection context, NpgsqlCommand command )
+        public override void Query( NpgsqlConnector context, NpgsqlCommand command )
         {
 
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Query");
@@ -80,7 +80,7 @@ namespace Npgsql
 
         }
 
-        public override void Parse(NpgsqlConnection context, NpgsqlParse parse)
+        public override void Parse(NpgsqlConnector context, NpgsqlParse parse)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Parse");
             BufferedStream stream = new BufferedStream(context.Stream);
@@ -89,7 +89,7 @@ namespace Npgsql
         }
 
 
-        public override void Sync(NpgsqlConnection context)
+        public override void Sync(NpgsqlConnector context)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Sync");
             _syncMessage.WriteToStream(context.Stream, context.Encoding);
@@ -97,14 +97,14 @@ namespace Npgsql
             ProcessBackendResponses(context);
         }
 
-        public override void Flush(NpgsqlConnection context)
+        public override void Flush(NpgsqlConnector context)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Flush");
             _flushMessage.WriteToStream(context.Stream, context.Encoding);
             ProcessBackendResponses(context);
         }
 
-        public override void Bind(NpgsqlConnection context, NpgsqlBind bind)
+        public override void Bind(NpgsqlConnector context, NpgsqlBind bind)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Bind");
             BufferedStream stream = new BufferedStream(context.Stream);
@@ -113,7 +113,7 @@ namespace Npgsql
 
         }
 
-        public override void Execute(NpgsqlConnection context, NpgsqlExecute execute)
+        public override void Execute(NpgsqlConnector context, NpgsqlExecute execute)
         {
 
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Execute");
@@ -125,5 +125,21 @@ namespace Npgsql
             Sync(context);
         }
 
+        public override void Close( NpgsqlConnector context )
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");   
+            Stream stream = context.Stream;
+            stream.WriteByte((Byte)'X');
+            if (context.BackendProtocolVersion >= ProtocolVersion.Version3)
+                PGUtil.WriteInt32(stream, 4);
+            stream.Flush();
+
+            try {
+                stream.Close();
+            } catch {}
+
+            context.Stream = null;
+            ChangeState( context, NpgsqlClosedState.Instance );
+        }
     }
 }

+ 21 - 19
mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs

@@ -23,14 +23,14 @@
 // License along with this library; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-
-
 using System;
 using System.Collections;
 using System.IO;
 using System.Text;
 using System.Net;
 
+using NpgsqlTypes;
+
 namespace Npgsql
 {
 
@@ -43,13 +43,14 @@ namespace Npgsql
     // Should it be a struct or a class?
     internal struct NpgsqlRowDescriptionFieldData
     {
-        public String 	name;                      // Protocol 2/3
-        public Int32    table_oid;                 // Protocol 3
-        public Int16    column_attribute_number;   // Protocol 3
-        public Int32		type_oid;                  // Protocol 2/3
-        public Int16		type_size;                 // Protocol 2/3
-        public Int32		type_modifier;		         // Protocol 2/3
-        public FormatCode    format_code;               // Protocol 3. 0 text, 1 binary
+        public String                   name;                      // Protocol 2/3
+        public Int32                    table_oid;                 // Protocol 3
+        public Int16                    column_attribute_number;   // Protocol 3
+        public Int32                    type_oid;                  // Protocol 2/3
+        public Int16                    type_size;                 // Protocol 2/3
+        public Int32                    type_modifier;		       // Protocol 2/3
+        public FormatCode               format_code;               // Protocol 3. 0 text, 1 binary
+        public NpgsqlBackendTypeInfo    type_info;                 // everything we know about this field type
     }
 
     /// <summary>
@@ -63,32 +64,31 @@ namespace Npgsql
         private static readonly String CLASSNAME = "NpgsqlRowDescription";
 
 
-        private ArrayList	fields_data = new ArrayList();
-
-        private ArrayList   fields_index = new ArrayList();
+        private ArrayList                fields_data = new ArrayList();
+        private ArrayList                fields_index = new ArrayList();
 
-        private ProtocolVersion protocol_version;
+        private ProtocolVersion          protocol_version;
 
         public NpgsqlRowDescription(ProtocolVersion protocolVersion)
         {
             protocol_version = protocolVersion;
         }
 
-        public void ReadFromStream(Stream input_stream, Encoding encoding)
+        public void ReadFromStream(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping)
         {
             switch (protocol_version) {
             case ProtocolVersion.Version2 :
-                ReadFromStream_Ver_2(input_stream, encoding);
+                ReadFromStream_Ver_2(input_stream, encoding, type_mapping);
                 break;
 
             case ProtocolVersion.Version3 :
-                ReadFromStream_Ver_3(input_stream, encoding);
+                ReadFromStream_Ver_3(input_stream, encoding, type_mapping);
                 break;
 
             }
         }
 
-        public void ReadFromStream_Ver_2(Stream input_stream, Encoding encoding)
+        private void ReadFromStream_Ver_2(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
 
@@ -114,6 +114,7 @@ namespace Npgsql
                 input_stream.Read(input_buffer, 0, 4 + 2 + 4);
 
                 fd.type_oid = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
+                fd.type_info = type_mapping[fd.type_oid];
                 fd.type_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 4));
                 fd.type_modifier = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 6));
 
@@ -124,7 +125,7 @@ namespace Npgsql
             }
         }
 
-        public void ReadFromStream_Ver_3(Stream input_stream, Encoding encoding)
+        private void ReadFromStream_Ver_3(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
 
@@ -146,6 +147,7 @@ namespace Npgsql
                 fd.table_oid = PGUtil.ReadInt32(input_stream, input_buffer);
                 fd.column_attribute_number = PGUtil.ReadInt16(input_stream, input_buffer);
                 fd.type_oid = PGUtil.ReadInt32(input_stream, input_buffer);
+                fd.type_info = type_mapping[fd.type_oid];
                 fd.type_size = PGUtil.ReadInt16(input_stream, input_buffer);
                 fd.type_modifier = PGUtil.ReadInt32(input_stream, input_buffer);
                 fd.format_code = (FormatCode)PGUtil.ReadInt16(input_stream, input_buffer);
@@ -185,7 +187,7 @@ namespace Npgsql
                 result++;
             }
 
-            return -1;
+            throw new ArgumentOutOfRangeException("fieldName", fieldName, "Field name not found"); 
         }
 
     }

+ 1 - 1
mcs/class/Npgsql/Npgsql/NpgsqlStartupState.cs

@@ -51,7 +51,7 @@ namespace Npgsql
                 return _instance;
             }
         }
-        public override void Authenticate( NpgsqlConnection context, string password)
+        public override void Authenticate( NpgsqlConnector context, string password)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Authenticate");
             NpgsqlPasswordPacket pwpck = new NpgsqlPasswordPacket(password, context.BackendProtocolVersion);

+ 28 - 52
mcs/class/Npgsql/Npgsql/NpgsqlState.cs

@@ -43,91 +43,69 @@ namespace Npgsql
     internal abstract class NpgsqlState
     {
         private readonly String CLASSNAME = "NpgsqlState";
-        protected ResourceManager resman = null;
+        protected static ResourceManager resman = new ResourceManager(typeof(NpgsqlState));
 
-        public virtual void Open(NpgsqlConnection context)
+        public virtual void Open(NpgsqlConnector context)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Startup(NpgsqlConnection context)
+        public virtual void Startup(NpgsqlConnector context)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Authenticate(NpgsqlConnection context, string password)
+        public virtual void Authenticate(NpgsqlConnector context, string password)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Query(NpgsqlConnection context, NpgsqlCommand command)
+        public virtual void Query(NpgsqlConnector context, NpgsqlCommand command)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Ready( NpgsqlConnection context )
+        public virtual void Ready( NpgsqlConnector context )
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void FunctionCall(NpgsqlConnection context, NpgsqlCommand command)
+        public virtual void FunctionCall(NpgsqlConnector context, NpgsqlCommand command)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Parse(NpgsqlConnection context, NpgsqlParse parse)
+        public virtual void Parse(NpgsqlConnector context, NpgsqlParse parse)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Flush(NpgsqlConnection context)
+        public virtual void Flush(NpgsqlConnector context)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Sync(NpgsqlConnection context)
+        public virtual void Sync(NpgsqlConnector context)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Bind(NpgsqlConnection context, NpgsqlBind bind)
+        public virtual void Bind(NpgsqlConnector context, NpgsqlBind bind)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
-        public virtual void Execute(NpgsqlConnection context, NpgsqlExecute execute)
+        public virtual void Execute(NpgsqlConnector context, NpgsqlExecute execute)
         {
             throw new InvalidOperationException("Internal Error! " + this);
         }
 
-        public NpgsqlState()
+        public virtual void Close( NpgsqlConnector context )
         {
-            resman = new ResourceManager(this.GetType());
-        }
+            if (this != NpgsqlClosedState.Instance) {
+                try {
+                    context.Stream.Close();
+                } catch {}
 
-        public virtual void Close( NpgsqlConnection context )
-        {
-            /*NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");
-            if ( context.State == ConnectionState.Open )
-            {
-                Stream stream = context.Stream;
-                if ( stream.CanWrite )
-                {
-                    stream.WriteByte((Byte)'X');
-                    if (context.BackendProtocolVersion >= ProtocolVersion.Version3)
-                        PGUtil.WriteInt32(stream, 4);
-                    stream.Flush();
-                }
-            }*/
-
-            // CHECKME!!!
-            // The close logic is pretty messed up I think.  Needs lots of work.
-/*
-            if (! context.Connector.Shared) {
-                if (context.Connector.Stream != null) {
-                    try {
-                        context.Connector.Stream.Close();
-                    } catch {}
-                }
+                context.Stream = null;
+                ChangeState( context, NpgsqlClosedState.Instance );
             }
-*/
-            //ChangeState( context, NpgsqlClosedState.Instance );
         }
 
         ///<summary>
         ///This method is used by the states to change the state of the context.
         /// </summary>
-        protected virtual void ChangeState(NpgsqlConnection context, NpgsqlState newState)
+        protected virtual void ChangeState(NpgsqlConnector context, NpgsqlState newState)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeState");
             context.CurrentState = newState;
@@ -140,11 +118,9 @@ namespace Npgsql
         /// to handle backend requests.
         /// </summary>
         ///
-        protected virtual void ProcessBackendResponses( NpgsqlConnection context )
+        protected virtual void ProcessBackendResponses( NpgsqlConnector context )
         {
-            // reset any responses just before getting new ones
-            context.Mediator.ResetResponses();
-
+            
             try {
                 switch (context.BackendProtocolVersion) {
                 case ProtocolVersion.Version2 :
@@ -162,7 +138,7 @@ namespace Npgsql
             }
         }
 
-        protected virtual void ProcessBackendResponses_Ver_2( NpgsqlConnection context )
+        protected virtual void ProcessBackendResponses_Ver_2( NpgsqlConnector context )
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");
 
@@ -301,7 +277,7 @@ namespace Npgsql
 
                     {
                         NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
-                        rd.ReadFromStream(stream, context.Encoding);
+                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);
 
                         // Initialize the array list which will contain the data from this rowdescription.
                         mediator.AddRowDescription(rd);
@@ -315,7 +291,7 @@ namespace Npgsql
                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");
 
                     {
-                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.OidToNameMapping, context.BackendProtocolVersion);
+                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion);
                         asciiRow.ReadFromStream(stream, context.Encoding);
 
                         // Add this row to the rows array.
@@ -427,7 +403,7 @@ namespace Npgsql
             }
         }
 
-        protected virtual void ProcessBackendResponses_Ver_3( NpgsqlConnection context )
+        protected virtual void ProcessBackendResponses_Ver_3( NpgsqlConnector context )
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");
 
@@ -568,7 +544,7 @@ namespace Npgsql
                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
                     {
                         NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
-                        rd.ReadFromStream(stream, context.Encoding);
+                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);
 
                         mediator.AddRowDescription(rd);
                     }
@@ -580,7 +556,7 @@ namespace Npgsql
                     // This is the AsciiRow message.
                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
                     {
-                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.OidToNameMapping, context.BackendProtocolVersion);
+                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion);
                         asciiRow.ReadFromStream(stream, context.Encoding);
 
                         // Add this row to the rows array.

+ 7 - 0
mcs/class/Npgsql/Npgsql/NpgsqlState.resx

@@ -127,4 +127,11 @@
     <data name="Log_ParameterStatus">
 		<value>ParameterStatus message from Server: Param = {0}, Value = {1}</value>
 	</data>
+	<data name="Log_ConnectedTo">
+                <value>Connected to: {0}:{1}.</value>
+        </data>
+
+	<data name="Log_QuerySent">
+                <value>Query sent: {0}.</value>
+        </data>
 </root>

+ 40 - 12
mcs/class/Npgsql/Npgsql/NpgsqlTransaction.cs

@@ -26,6 +26,7 @@
 
 using System;
 using System.Text;
+using System.Resources;
 using System.Data;
 
 
@@ -37,11 +38,11 @@ namespace Npgsql
     public sealed class NpgsqlTransaction : MarshalByRefObject, IDbTransaction
     {
         private static readonly String CLASSNAME = "NpgsqlTransaction";
+        private static ResourceManager resman = new ResourceManager(typeof(NpgsqlTransaction));
 
-        private NpgsqlConnection	_conn				= null;
-        private IsolationLevel		_isolation	= IsolationLevel.ReadCommitted;
-        private bool _disposing = false;
-        private System.Resources.ResourceManager resman;
+        private NpgsqlConnection    _conn = null;
+        private IsolationLevel      _isolation = IsolationLevel.ReadCommitted;
+        private bool                _disposing = false;
 
         internal NpgsqlTransaction(NpgsqlConnection conn) : this(conn, IsolationLevel.ReadCommitted)
         {}
@@ -67,10 +68,9 @@ namespace Npgsql
 
             commandText.Append("; BEGIN");
 
-
-            NpgsqlCommand command = new NpgsqlCommand(commandText.ToString(), conn);
+            NpgsqlCommand command = new NpgsqlCommand(commandText.ToString(), conn.Connector);
             command.ExecuteNonQuery();
-            _conn.InTransaction = true;
+            _conn.Connector.Transaction = this;
         }
 
         /// <summary>
@@ -106,6 +106,10 @@ namespace Npgsql
         {
             get
             {
+                if (_conn == null) {
+                    throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
+                }
+
                 return _isolation;
             }
         }
@@ -125,7 +129,7 @@ namespace Npgsql
             if(disposing && this._conn != null)
             {
                 this._disposing = true;
-                if (_conn.InTransaction)
+                if (_conn.Connector.Transaction != null)
                     this.Rollback();
             }
         }
@@ -135,10 +139,16 @@ namespace Npgsql
         /// </summary>
         public void Commit()
         {
+            if (_conn == null) {
+                throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
+            }
+
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Commit");
-            NpgsqlCommand command = new NpgsqlCommand("COMMIT", _conn);
+
+            NpgsqlCommand command = new NpgsqlCommand("COMMIT", _conn.Connector);
             command.ExecuteNonQuery();
-            _conn.InTransaction = false;
+            _conn.Connector.Transaction = null;
+            _conn = null;
         }
 
         /// <summary>
@@ -146,10 +156,28 @@ namespace Npgsql
         /// </summary>
         public void Rollback()
         {
+            if (_conn == null) {
+                throw new InvalidOperationException(resman.GetString("Exception_NoTransaction"));
+            }
+
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Rollback");
-            NpgsqlCommand command = new NpgsqlCommand("ROLLBACK", _conn);
+
+            NpgsqlCommand command = new NpgsqlCommand("ROLLBACK", _conn.Connector);
             command.ExecuteNonQuery();
-            _conn.InTransaction = false;
+            _conn.Connector.Transaction = null;
+            _conn = null;
+        }
+
+        /// <summary>
+        /// Cancel the transaction without telling the backend about it.  This is
+        /// used to make the transaction go away when closing a connection.
+        /// </summary>
+        internal void Cancel()
+        {
+            if (_conn != null) {
+                _conn.Connector.Transaction = null;
+                _conn = null;
+            }
         }
 
         internal bool Disposing{

+ 3 - 0
mcs/class/Npgsql/Npgsql/NpgsqlTransaction.resx

@@ -100,4 +100,7 @@
 	<data name="Exception_UnsopportedIsolationLevel">
 		<value>Must be Read Committed or Serializable.</value>
 	</data>
+	<data name="Exception_NoTransaction">
+		<value>No transaction in progress.</value>
+	</data>
 </root>

+ 1 - 0
mcs/class/Npgsql/Npgsql/PGUtil.cs

@@ -39,6 +39,7 @@ namespace Npgsql
     /// </summary>
     public enum ProtocolVersion
     {
+        Unknown,
         Version2,
         Version3
     }

+ 54 - 0
mcs/class/Npgsql/NpgsqlTypes/NpgsqlDbType.cs

@@ -0,0 +1,54 @@
+// NpgsqlTypes.NpgsqlDbType.cs
+// 
+// Author:
+//	Francisco Jr. ([email protected])
+//
+//	Copyright (C) 2002 The Npgsql Development Team
+//	[email protected]
+//	http://gborg.postgresql.org/project/npgsql/projdisplay.php
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+// 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+using System;
+using Npgsql;
+
+namespace NpgsqlTypes
+{
+  public enum NpgsqlDbType
+  {
+    Bigint,
+    Boolean,
+    Box,
+    Bytea,
+    Circle,
+    Date,
+    Double,
+    Integer,
+    Line,
+    LSeg,
+    Money,
+    Numeric,
+    Path,
+    Point,
+    Polygon,
+    Real,
+    Smallint,
+    Text,
+    Time,
+    Timestamp
+    
+  }
+  
+}

+ 488 - 0
mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs

@@ -0,0 +1,488 @@
+// NpgsqlTypes.NpgsqlTypesHelper.cs
+//
+// Author:
+//	Glen Parker <[email protected]>
+//
+//	Copyright (C) 2004 The Npgsql Development Team
+//	[email protected]
+//	http://gborg.postgresql.org/project/npgsql/projdisplay.php
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+// This file provides data type converters between PostgreSQL representations
+// and .NET objects.
+
+using System;
+using System.Collections;
+using System.Globalization;
+using System.Text;
+using System.IO;
+using System.Text.RegularExpressions;
+
+using Npgsql;
+
+namespace NpgsqlTypes
+{
+    /// <summary>
+    /// Provide event handlers to convert all native supported basic data types from their backend
+    /// text representation to a .NET object.
+    /// </summary>
+    internal abstract class BasicBackendToNativeTypeConverter
+    {
+        private static readonly String[] DateFormats = new String[] 
+        {
+          "yyyy-MM-dd",
+        };
+
+        private static readonly String[] TimeFormats = new String[]
+        {
+          "HH:mm:ss.ffffff",
+          "HH:mm:ss.fffff",	
+          "HH:mm:ss.ffff",
+          "HH:mm:ss.fff",
+          "HH:mm:ss.ff",
+          "HH:mm:ss.f",
+          "HH:mm:ss",
+          "HH:mm:ss.ffffffzz",
+          "HH:mm:ss.fffffzz",	
+          "HH:mm:ss.ffffzz",
+          "HH:mm:ss.fffzz",
+          "HH:mm:ss.ffzz",
+          "HH:mm:ss.fzz",
+          "HH:mm:sszz"
+        };
+
+        private static readonly String[] DateTimeFormats = new String[] 
+        {
+          "yyyy-MM-dd HH:mm:ss.ffffff",
+          "yyyy-MM-dd HH:mm:ss.fffff",	
+          "yyyy-MM-dd HH:mm:ss.ffff",
+          "yyyy-MM-dd HH:mm:ss.fff",
+          "yyyy-MM-dd HH:mm:ss.ff",
+          "yyyy-MM-dd HH:mm:ss.f",
+          "yyyy-MM-dd HH:mm:ss",
+          "yyyy-MM-dd HH:mm:ss.ffffffzz",
+          "yyyy-MM-dd HH:mm:ss.fffffzz",	
+          "yyyy-MM-dd HH:mm:ss.ffffzz",
+          "yyyy-MM-dd HH:mm:ss.fffzz",
+          "yyyy-MM-dd HH:mm:ss.ffzz",
+          "yyyy-MM-dd HH:mm:ss.fzz",
+          "yyyy-MM-dd HH:mm:sszz"
+        };
+
+        /// <summary>
+        /// Binary data.
+        /// </summary>
+        internal static Object ToBinary(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            Int32           octalValue = 0;
+            Int32           byteAPosition = 0;
+            Int32           byteAStringLength = BackendData.Length;
+            MemoryStream    ms = new MemoryStream();
+
+            while (byteAPosition < byteAStringLength)
+            {
+                // The IsDigit is necessary in case we receive a \ as the octal value and not
+                // as the indicator of a following octal value in decimal format.
+                // i.e.: \201\301P\A
+                if (BackendData[byteAPosition] == '\\') {
+
+                    if (byteAPosition + 1 == byteAStringLength)
+                    {
+                        octalValue = '\\';
+                        byteAPosition++;
+                    }
+                    else if (Char.IsDigit(BackendData[byteAPosition + 1]))
+                    {
+                        octalValue = (Byte.Parse(BackendData[byteAPosition + 1].ToString()) << 6);
+                        octalValue |= (Byte.Parse(BackendData[byteAPosition + 2].ToString()) << 3);
+                        octalValue |= Byte.Parse(BackendData[byteAPosition + 3].ToString());
+                        byteAPosition += 4;
+
+                    }
+                    else
+                    {
+                        octalValue = '\\';
+                        byteAPosition += 2;
+                    }
+
+                } else {
+                    octalValue = (Byte)BackendData[byteAPosition];
+                    byteAPosition++;
+                }
+
+
+                ms.WriteByte((Byte)octalValue);
+
+            }
+
+            return ms.ToArray();
+        }
+
+        /// <summary>
+        /// Convert a postgresql boolean to a System.Boolean.
+        /// </summary>
+        internal static Object ToBoolean(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            return (BackendData.ToLower() == "t" ? true : false);
+        }
+
+        /// <summary>
+        /// Convert a postgresql datetime to a System.DateTime.
+        /// </summary>
+        internal static Object ToDateTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            // Get the date time parsed in all expected formats for timestamp.
+            return DateTime.ParseExact(BackendData,
+                                        DateTimeFormats,
+                                        DateTimeFormatInfo.InvariantInfo,
+                                        DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
+        }
+
+        /// <summary>
+        /// Convert a postgresql date to a System.DateTime.
+        /// </summary>
+        internal static Object ToDate(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            return DateTime.ParseExact(BackendData,
+                                        DateFormats,
+                                        DateTimeFormatInfo.InvariantInfo,
+                                        DateTimeStyles.AllowWhiteSpaces);
+        }
+
+        /// <summary>
+        /// Convert a postgresql time to a System.DateTime.
+        /// </summary>
+        internal static Object ToTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            return DateTime.ParseExact(BackendData,
+                                        TimeFormats,
+                                        DateTimeFormatInfo.InvariantInfo,
+                                        DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
+        }
+
+        /// <summary>
+        /// Convert a postgresql money to a System.Decimal.
+        /// </summary>
+        internal static Object ToMoney(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            // It's a number with a $ on the beginning...
+            return Convert.ToDecimal(BackendData.Substring(1, BackendData.Length - 1), CultureInfo.InvariantCulture);
+        }
+    }
+
+    /// <summary>
+    /// Provide event handlers to convert the basic native supported data types from
+    /// native form to backend representation.
+    /// </summary>
+    internal abstract class BasicNativeToBackendTypeConverter
+    {
+        /// <summary>
+        /// String data.
+        /// </summary>
+        internal static String ToString(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            return NativeData.ToString().Replace("'", "''");
+        }
+
+        /// <summary>
+        /// Binary data.
+        /// </summary>
+        internal static String ToBinary(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            Byte[]     byteArray = (Byte[])NativeData;
+            int        len = byteArray.Length;
+            char[]     res = new char[len * 5];
+
+            for (int i = 0, o = 0; i < len; ++i, o += 5)
+            {
+                byte item = byteArray[i];
+                res[o] = res[o + 1] = '\\';
+                res[o + 2] = (char)('0' + (7 & (item >> 6)));
+                res[o + 3] = (char)('0' + (7 & (item >> 3)));
+                res[o + 4] = (char)('0' + (7 & item));
+            }
+
+            return new String(res);
+        }
+
+        /// <summary>
+        /// Convert to a postgresql boolean.
+        /// </summary>
+        internal static String ToBoolean(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            return ((bool)NativeData) ? "TRUE" : "FALSE";
+        }
+
+        /// <summary>
+        /// Convert to a postgresql timestamp.
+        /// </summary>
+        internal static String ToDateTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            return ((DateTime)NativeData).ToString("yyyy-MM-dd HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo);
+        }
+
+        /// <summary>
+        /// Convert to a postgresql date.
+        /// </summary>
+        internal static String ToDate(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            return ((DateTime)NativeData).ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
+        }
+
+        /// <summary>
+        /// Convert to a postgresql time.
+        /// </summary>
+        internal static String ToTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            return ((DateTime)NativeData).ToString("HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo);
+        }
+
+        /// <summary>
+        /// Convert to a postgres money.
+        /// </summary>
+        internal static String ToMoney(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            return "$" + ((Decimal)NativeData).ToString(DateTimeFormatInfo.InvariantInfo);
+        }
+    }
+
+
+    /// <summary>
+    /// Provide event handlers to convert extended native supported data types from their backend
+    /// text representation to a .NET object.
+    /// </summary>
+    internal abstract class ExtendedBackendToNativeTypeConverter
+    {
+    
+    	private static readonly Regex pointRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)");
+    	private static readonly Regex boxlsegRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)");
+    	private static readonly Regex pathpolygonRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)");
+    	private static readonly Regex circleRegex = new Regex(@"<\((-?\d+.?\d*),(-?\d+.?\d*)\),(\d+.?\d*)>");
+    	
+    	
+        /// <summary>
+        /// Convert a postgresql point to a System.NpgsqlPoint.
+        /// </summary>
+        internal static Object ToPoint(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            
+            Match m = pointRegex.Match(BackendData);
+            
+            return new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString()));
+            
+            
+            
+        }
+
+        /// <summary>
+        /// Convert a postgresql point to a System.RectangleF.
+        /// </summary>
+        internal static Object ToBox(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            
+            Match m = boxlsegRegex.Match(BackendData);
+            
+            return new NpgsqlBox(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())),
+            						new NpgsqlPoint(Single.Parse(m.Groups[3].ToString()), Single.Parse(m.Groups[4].ToString())));
+        }
+
+        /// <summary>
+        /// LDeg.
+        /// </summary>
+        internal static Object ToLSeg(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            Match m = boxlsegRegex.Match(BackendData);
+            
+            return new NpgsqlLSeg(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())),
+            						new NpgsqlPoint(Single.Parse(m.Groups[3].ToString()), Single.Parse(m.Groups[4].ToString())));
+        }
+
+        /// <summary>
+        /// Path.
+        /// </summary>
+        internal static Object ToPath(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+        	   	
+        	Match m = pathpolygonRegex.Match(BackendData);
+        	Boolean open = (BackendData[0] == '['); 
+        	ArrayList points = new ArrayList();
+        	    	
+        	while (m.Success)
+        	{
+        		
+        		if (open)
+        			points.Add(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())));
+        		else
+        		{
+        			// Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with
+        			// a trailling ')' only when the last character of the string is a ')' which is the case for closed paths
+					// returned by backend. This gives parsing exception when converting to single. 
+					// I still don't know if this is a bug in mono or in my regular expression.
+        			// Check if there is this character and remove it.
+        			
+        			String group2 = m.Groups[2].ToString();
+        			if (group2.EndsWith(")"))
+        				group2 = group2.Remove(group2.Length - 1, 1);
+        				
+        			points.Add(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(group2)));
+        		}
+        			
+        		m = m.NextMatch();
+        		
+        	}
+        	
+        	NpgsqlPath result = new NpgsqlPath((NpgsqlPoint[]) points.ToArray(typeof(NpgsqlPoint)));
+			result.IsOpen = open; 
+        	return result;
+        	
+            
+        }
+
+        /// <summary>
+        /// Polygon.
+        /// </summary>
+        internal static Object ToPolygon(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            
+        	Match m = pathpolygonRegex.Match(BackendData);
+        	ArrayList points = new ArrayList();
+        	    	
+        	while (m.Success)
+        	{
+        		
+	   			// Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with
+	   			// a trailling ')' only when the last character of the string is a ')' which is the case for closed paths
+				// returned by backend. This gives parsing exception when converting to single. 
+				// I still don't know if this is a bug in mono or in my regular expression.
+	   			// Check if there is this character and remove it.
+	   			
+	   			String group2 = m.Groups[2].ToString();
+	   			if (group2.EndsWith(")"))
+	   				group2 = group2.Remove(group2.Length - 1, 1);
+	   				
+	   			points.Add(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(group2)));
+
+	        			
+	       		m = m.NextMatch();
+        		
+        	}
+        	
+        	return new NpgsqlPolygon((NpgsqlPoint[]) points.ToArray(typeof(NpgsqlPoint)));
+			
+        }
+
+        /// <summary>
+        /// Circle.
+        /// </summary>
+        internal static Object ToCircle(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+        	Match m = circleRegex.Match(BackendData);
+            return new NpgsqlCircle(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())), Single.Parse(m.Groups[3].ToString()));
+            
+        }
+    }
+
+    /// <summary>
+    /// Provide event handlers to convert extended native supported data types from
+    /// native form to backend representation.
+    /// </summary>
+    internal abstract class ExtendedNativeToBackendTypeConverter
+    {
+        /// <summary>
+        /// Point.
+        /// </summary>
+        internal static String ToPoint(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            if (NativeData is NpgsqlPoint)
+           	{
+           		NpgsqlPoint	P = (NpgsqlPoint)NativeData;
+                return String.Format(CultureInfo.InvariantCulture, "({0},{1})", P.X, P.Y);
+           	} 
+			else 
+			{
+                throw new InvalidCastException("Unable to cast data to NpgsqlPoint type");
+            }
+        }
+
+        /// <summary>
+        /// Box.
+        /// </summary>
+        internal static String ToBox(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            /*if (NativeData.GetType() == typeof(Rectangle)) {
+                Rectangle       R = (Rectangle)NativeData;
+                return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", R.Left, R.Top, R.Left + R.Width, R.Top + R.Height);
+            } else if (NativeData.GetType() == typeof(RectangleF)) {
+                RectangleF      R = (RectangleF)NativeData;
+                return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", R.Left, R.Top, R.Left + R.Width, R.Top + R.Height);*/
+                
+            if (NativeData is NpgsqlBox)
+			{
+				NpgsqlBox box = (NpgsqlBox) NativeData;
+				return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", box.LowerLeft.X, box.LowerLeft.Y, box.UpperRight.X, box.UpperRight.Y);
+			    
+            } else {
+                throw new InvalidCastException("Unable to cast data to Rectangle type");
+            }
+        }
+
+        /// <summary>
+        /// LSeg.
+        /// </summary>
+        internal static String ToLSeg(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            NpgsqlLSeg      S = (NpgsqlLSeg)NativeData;
+            return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", S.Start.X, S.Start.Y, S.End.X, S.End.Y);
+        }
+
+        /// <summary>
+        /// Open path.
+        /// </summary>
+        internal static String ToPath(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            StringBuilder   B = new StringBuilder();
+
+            foreach (NpgsqlPoint P in ((NpgsqlPath)NativeData).Points) {
+                B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y);
+            }
+
+            return String.Format("[{0}]", B.ToString());
+        }
+
+        /// <summary>
+        /// Polygon.
+        /// </summary>
+        internal static String ToPolygon(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            StringBuilder   B = new StringBuilder();
+
+            foreach (NpgsqlPoint P in ((NpgsqlPolygon)NativeData).Points) {
+                B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y);
+            }
+
+            return String.Format("({0})", B.ToString());
+        }
+
+        /// <summary>
+        /// Circle.
+        /// </summary>
+        internal static String ToCircle(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+        {
+            NpgsqlCircle      C = (NpgsqlCircle)NativeData;
+            return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", C.Center.X, C.Center.Y, C.Radius);
+        }
+    }
+}

+ 210 - 0
mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypes.cs

@@ -0,0 +1,210 @@
+// NpgsqlTypes.NpgsqlTypesHelper.cs
+//
+// Author:
+//	Glen Parker <[email protected]>
+//
+//	Copyright (C) 2004 The Npgsql Development Team
+//	[email protected]
+//	http://gborg.postgresql.org/project/npgsql/projdisplay.php
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+// This file provides implementations of PostgreSQL specific data types that cannot
+// be mapped to standard .NET classes.
+
+using System;
+using System.Collections;
+using System.Globalization;
+using System.Data;
+using System.Net;
+using System.Text;
+using System.IO;
+using System.Resources;
+using System.Drawing;
+
+namespace NpgsqlTypes
+{
+
+	/// <summary>
+	/// Represents a PostgreSQL Point type
+	/// </summary>
+	
+	public struct NpgsqlPoint
+	{
+		private Single _X;
+		private Single _Y;
+		
+		public NpgsqlPoint(Single X, Single Y)
+		{
+			_X = X;
+			_Y = Y;
+		}
+		
+		public Single X
+		{
+			get
+			{
+				return _X;
+			}
+			
+			set
+			{
+				_X = value;
+			}
+		}
+		
+		
+		public Single Y
+		{
+			get
+			{
+				return _Y;
+			}
+			
+			set
+			{
+				_Y = value;
+			}
+		}
+	}
+	
+	public struct NpgsqlBox
+	{
+		private NpgsqlPoint _UpperRight;
+		private NpgsqlPoint _LowerLeft;
+		
+		public NpgsqlBox(NpgsqlPoint UpperRight, NpgsqlPoint LowerLeft)
+		{
+			_UpperRight = UpperRight;
+			_LowerLeft = LowerLeft;
+		}
+		
+
+		public NpgsqlPoint UpperRight
+		{
+			get
+			{
+				return _UpperRight;
+			}
+			set
+			{
+				_UpperRight = value;
+			}
+		}
+
+		public NpgsqlPoint LowerLeft
+		{
+			get
+			{
+				return _LowerLeft;
+			}
+			set
+			{
+				_LowerLeft = value;
+			}
+		} 
+		
+	}
+	
+	
+    /// <summary>
+    /// Represents a PostgreSQL Line Segment type.
+    /// </summary>
+    public struct NpgsqlLSeg
+    {
+        public NpgsqlPoint     Start;
+        public NpgsqlPoint     End;
+
+        public NpgsqlLSeg(NpgsqlPoint Start, NpgsqlPoint End)
+        {
+            this.Start = Start;
+            this.End = End;
+        }
+
+        public override String ToString()
+        {
+            return String.Format("({0}, {1})", Start, End);
+        }
+    }
+
+    /// <summary>
+    /// Represents a PostgreSQL Path type.
+    /// </summary>
+    public struct NpgsqlPath
+    {
+        internal NpgsqlPoint[]	Points;
+        
+        internal Boolean 		IsOpen;			
+
+        public NpgsqlPath(NpgsqlPoint[] Points)
+        {
+            this.Points = Points;
+            IsOpen = false;
+        }
+
+        public Int32 Count
+        { get { return Points.Length; } }
+
+        public NpgsqlPoint this [Int32 Index]
+        { get { return Points[Index]; } }
+        
+        public Boolean Open
+        {
+        	get
+        	{
+        		return IsOpen;
+        	}
+        }
+    }
+
+    /// <summary>
+    /// Represents a PostgreSQL Polygon type.
+    /// </summary>
+    public struct NpgsqlPolygon
+    {
+        internal NpgsqlPoint[]     Points;
+
+        public NpgsqlPolygon(NpgsqlPoint[] Points)
+        {
+            this.Points = Points;
+        }
+
+        public Int32 Count
+        { get { return Points.Length; } }
+
+        public NpgsqlPoint this [Int32 Index]
+        { get { return Points[Index]; } }
+    }
+
+    /// <summary>
+    /// Represents a PostgreSQL Circle type.
+    /// </summary>
+    public struct NpgsqlCircle
+    {
+        public NpgsqlPoint   Center;
+        public Double        Radius;
+
+        public NpgsqlCircle(NpgsqlPoint Center, Double Radius)
+        {
+            this.Center = Center;
+            this.Radius = Radius;
+        }
+
+        public override String ToString()
+        {
+            return string.Format("({0}), {1}", Center, Radius);
+        }
+    }
+}

+ 701 - 396
mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs

@@ -1,4 +1,3 @@
-
 // NpgsqlTypes.NpgsqlTypesHelper.cs
 //
 // Author:
@@ -28,534 +27,840 @@ using System.Globalization;
 using System.Data;
 using System.Net;
 using System.Text;
-using System.IO;
-using Npgsql;
 using System.Resources;
+using Npgsql;
 
 
 namespace NpgsqlTypes
 {
-
-    /*internal struct NpgsqlTypeMapping
-    {
-      public String        _backendTypeName;
-      public Type          _frameworkType;
-      public Int32         _typeOid;
-      public NpgsqlDbType  _npgsqlDbType;
-      
-      public NpgsqlTypeMapping(String backendTypeName, Type frameworkType, Int32 typeOid, NpgsqlDbType npgsqlDbType)
-      {
-        _backendTypeName = backendTypeName;
-        _frameworkType = frameworkType;
-        _typeOid = typeOid;
-        _npgsqlDbType = npgsqlDbType;
-        
-      }
-    }*/
-
-
     /// <summary>
     ///	This class contains helper methods for type conversion between
     /// the .Net type system and postgresql.
     /// </summary>
-    internal class NpgsqlTypesHelper
+    internal abstract class NpgsqlTypesHelper
     {
-
-        private static Hashtable _oidToNameMappings = new Hashtable();
-
         // Logging related values
-        private static readonly String CLASSNAME = "NpgsqlDataReader";
+        private static readonly String CLASSNAME = "NpgsqlTypesHelper";
         private static ResourceManager resman = new ResourceManager(typeof(NpgsqlTypesHelper));
 
-        // From include/utils/datetime.h. Thanks to Carlos Guzman Alvarez
-        private static readonly DateTime postgresEpoch = new DateTime(2000, 1, 1);
-
-        private static readonly string[] DateFormats = new String[] 
-        {
-          "yyyy-MM-dd",
-        };
-
-        private static readonly string[] TimeFormats = new String[]
-        {
-          "HH:mm:ss.ffffff",
-          "HH:mm:ss.fffff",	
-          "HH:mm:ss.ffff",
-          "HH:mm:ss.fff",
-          "HH:mm:ss.ff",
-          "HH:mm:ss.f",
-          "HH:mm:ss",
-          "HH:mm:ss.ffffffzz",
-          "HH:mm:ss.fffffzz",	
-          "HH:mm:ss.ffffzz",
-          "HH:mm:ss.fffzz",
-          "HH:mm:ss.ffzz",
-          "HH:mm:ss.fzz",
-          "HH:mm:sszz"
-        };
-
-        private static readonly string[] DateTimeFormats = new String[] 
-        {
-          "yyyy-MM-dd HH:mm:ss.ffffff",
-          "yyyy-MM-dd HH:mm:ss.fffff",	
-          "yyyy-MM-dd HH:mm:ss.ffff",
-          "yyyy-MM-dd HH:mm:ss.fff",
-          "yyyy-MM-dd HH:mm:ss.ff",
-          "yyyy-MM-dd HH:mm:ss.f",
-          "yyyy-MM-dd HH:mm:ss",
-          "yyyy-MM-dd HH:mm:ss.ffffffzz",
-          "yyyy-MM-dd HH:mm:ss.fffffzz",	
-          "yyyy-MM-dd HH:mm:ss.ffffzz",
-          "yyyy-MM-dd HH:mm:ss.fffzz",
-          "yyyy-MM-dd HH:mm:ss.ffzz",
-          "yyyy-MM-dd HH:mm:ss.fzz",
-          "yyyy-MM-dd HH:mm:sszz"
-        };
-
-        public static String GetBackendTypeNameFromDbType(DbType dbType)
-        {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromDbType");
-
-            switch (dbType)
-            {
-            case DbType.Binary:
-                return "bytea";
-            case DbType.Boolean:
-                return "bool";
-            case DbType.Single:
-                return "float4";
-            case DbType.Double:
-                return "float8";
-            case DbType.Int64:
-                return "int8";
-            case DbType.Int32:
-                return "int4";
-            case DbType.Decimal:
-                return "numeric";
-            case DbType.Int16:
-                return "int2";
-            case DbType.String:
-            case DbType.AnsiString:
-                return "text";
-            case DbType.DateTime:
-                return "timestamp";
-            case DbType.Date:
-                return "date";
-            case DbType.Time:
-                return "time";
-            default:
-                throw new InvalidCastException(String.Format(resman.GetString("Exception_TypeNotSupported"), dbType));
+        /// <summary>
+        /// A cache of basic datatype mappings keyed by server version.  This way we don't
+        /// have to load the basic type mappings for every connection.
+        /// </summary>
+        private static Hashtable BackendTypeMappingCache = new Hashtable();
+        private static NpgsqlNativeTypeMapping NativeTypeMapping = null;
 
-            }
+
+        /// <summary>
+        /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
+        /// of the given NpgsqlDbType.
+        /// </summary>
+        public static NpgsqlNativeTypeInfo GetNativeTypeInfo(NpgsqlDbType NpgsqlDbType)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType");
+
+            VerifyDefaultTypesMap();
+            return NativeTypeMapping[NpgsqlDbType];
         }
+        
+        /// <summary>
+        /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
+        /// of the given DbType.
+        /// </summary>
+        public static NpgsqlNativeTypeInfo GetNativeTypeInfo(DbType DbType)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType");
 
-        public static Object ConvertBackendBytesToStytemType(Hashtable oidToNameMapping, Byte[] data, Encoding encoding, Int32 fieldValueSize, Int32 typeOid, Int32 typeModifier)
+            VerifyDefaultTypesMap();
+            return NativeTypeMapping[DbType];
+        }
+        
+        
+
+        /// <summary>
+        /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
+        /// of the given System.Type.
+        /// </summary>
+        public static NpgsqlNativeTypeInfo GetNativeTypeInfo(Type Type)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType");
+
+            VerifyDefaultTypesMap();
+            return NativeTypeMapping[Type];
+        }
+
+        // CHECKME
+        // Not sure what to do with this one.  I don't believe we ever ask for a binary
+        // formatting, so this shouldn't even be used right now.
+        // At some point this will need to be merged into the type converter system somehow?
+        public static Object ConvertBackendBytesToSystemType(NpgsqlBackendTypeInfo TypeInfo, Byte[] data, Encoding encoding, Int32 fieldValueSize, Int32 typeModifier)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendBytesToStytemType");
-            //[TODO] Find a way to eliminate this checking. It is just used at bootstrap time
-            // when connecting because we don't have yet loaded typeMapping. The switch below
-            // crashes with NullPointerReference when it can't find the typeOid.
 
-            if (!oidToNameMapping.ContainsKey(typeOid))
+            /*
+            // We are never guaranteed to know about every possible data type the server can send us.
+            // When we encounter an unknown type, we punt and return the data without modification.
+            if (TypeInfo == null)
                 return data;
 
-            switch ((DbType)oidToNameMapping[typeOid])
+            switch (TypeInfo.NpgsqlDbType)
             {
-            case DbType.Binary:
+            case NpgsqlDbType.Binary:
                 return data;
-            case DbType.Boolean:
+            case NpgsqlDbType.Boolean:
                 return BitConverter.ToBoolean(data, 0);
-            case DbType.DateTime:
+            case NpgsqlDbType.DateTime:
                 return DateTime.MinValue.AddTicks(IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0)));
 
-            case DbType.Int16:
+            case NpgsqlDbType.Int16:
                 return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 0));
-            case DbType.Int32:
+            case NpgsqlDbType.Int32:
                 return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 0));
-            case DbType.Int64:
+            case NpgsqlDbType.Int64:
                 return IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0));
-            case DbType.String:
-            case DbType.AnsiString:
-            case DbType.StringFixedLength:
+            case NpgsqlDbType.String:
+            case NpgsqlDbType.AnsiString:
+            case NpgsqlDbType.StringFixedLength:
                 return encoding.GetString(data, 0, fieldValueSize);
             default:
                 throw new InvalidCastException("Type not supported in binary format");
-            }
-
-
+            }*/
+            
+            return null;
         }
 
-        private static string QuoteString(bool Quote, string S)
+        ///<summary>
+        /// This method is responsible to convert the string received from the backend
+        /// to the corresponding NpgsqlType.
+        /// The given TypeInfo is called upon to do the conversion.
+        /// If no TypeInfo object is provided, no conversion is performed.
+        /// </summary>
+        public static Object ConvertBackendStringToSystemType(NpgsqlBackendTypeInfo TypeInfo, String data, Int16 typeSize, Int32 typeModifier)
         {
-            if (Quote) {
-                return string.Format("'{0}'", S);
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendStringToSystemType");
+
+            if (TypeInfo != null) {
+                return TypeInfo.ConvertToNative(data, typeSize, typeModifier);
             } else {
-                return S;
+                return data;
             }
         }
 
-        public static String ConvertNpgsqlParameterToBackendStringValue(NpgsqlParameter parameter, Boolean QuoteStrings)
+        /// <summary>
+        /// Create the one and only native to backend type map.
+        /// This map is used when formatting native data
+        /// types to backend representations.
+        /// </summary>
+        private static void VerifyDefaultTypesMap()
         {
-            // HACK (?)
-            // [email protected] 05/20/2004
-            // bool QuoteString is a bit of a hack.
-            // When using the version 3 extended query support, we do not need to do quoting of parameters.
-            // The backend handles that properly.
+            lock(CLASSNAME) {
+                if (NativeTypeMapping != null) {
+                    return;
+                }
 
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertNpgsqlParameterToBackendStringValue");
+                NativeTypeMapping = new NpgsqlNativeTypeMapping();
 
-            if ((parameter.Value == DBNull.Value) || (parameter.Value == null))
-                return "NULL";
+                NativeTypeMapping.AddType("text", NpgsqlDbType.Text, DbType.String, true,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToString));
 
-            switch(parameter.DbType)
-            {
-            case DbType.Binary:
-                return QuoteString(QuoteStrings, ConvertByteArrayToBytea((Byte[])parameter.Value));
+                NativeTypeMapping.AddDbTypeAlias("text", DbType.StringFixedLength);
+                NativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiString);
+                NativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiStringFixedLength);
+                NativeTypeMapping.AddTypeAlias("text", typeof(String));
 
-            case DbType.Boolean:
-                return ((bool)parameter.Value) ? "TRUE" : "FALSE";
+                NativeTypeMapping.AddType("bytea", NpgsqlDbType.Bytea, DbType.Binary, true,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBinary));
 
-            case DbType.Int64:
-            case DbType.Int32:
-            case DbType.Int16:
-                return parameter.Value.ToString();
+                NativeTypeMapping.AddTypeAlias("bytea", typeof(Byte[]));
 
-            case DbType.Single:
-                // To not have a value implicitly converted to float8, we add quotes.
-                return QuoteString(QuoteStrings, ((Single)parameter.Value).ToString(NumberFormatInfo.InvariantInfo));
+                NativeTypeMapping.AddType("bool", NpgsqlDbType.Boolean, DbType.Boolean, false,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBoolean));
 
-            case DbType.Double:
-                return ((Double)parameter.Value).ToString(NumberFormatInfo.InvariantInfo);
+                NativeTypeMapping.AddTypeAlias("bool", typeof(Boolean));
+                                
+                NativeTypeMapping.AddType("int2", NpgsqlDbType.Smallint, DbType.Int16, false,
+                null);
 
-            case DbType.Date:
-                return QuoteString(QuoteStrings, ((DateTime)parameter.Value).ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo));
+                NativeTypeMapping.AddTypeAlias("int2", typeof(Int16));
+                
+                NativeTypeMapping.AddType("int4", NpgsqlDbType.Integer, DbType.Int32, false,
+                null);
 
-            case DbType.Time:
-                return QuoteString(QuoteStrings, ((DateTime)parameter.Value).ToString("HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo));
+                NativeTypeMapping.AddTypeAlias("int4", typeof(Int32));
 
-            case DbType.DateTime:
-                return QuoteString(QuoteStrings, ((DateTime)parameter.Value).ToString("yyyy-MM-dd HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo));
+                NativeTypeMapping.AddType("int8", NpgsqlDbType.Bigint, DbType.Int64, false,
+                null);
 
-            case DbType.Decimal:
-                return ((Decimal)parameter.Value).ToString(NumberFormatInfo.InvariantInfo);
+                NativeTypeMapping.AddTypeAlias("int8", typeof(Int64));
 
-            case DbType.String:
-            case DbType.AnsiString:
-            case DbType.StringFixedLength:
-                return QuoteString(QuoteStrings, parameter.Value.ToString().Replace("'", "''"));
+                NativeTypeMapping.AddType("float4", NpgsqlDbType.Real, DbType.Single, false,
+                null);
 
-            default:
-                // This should not happen!
-                throw new InvalidCastException(String.Format(resman.GetString("Exception_TypeNotSupported"), parameter.DbType));
+                NativeTypeMapping.AddTypeAlias("float4", typeof(Single));
 
-            }
+                NativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, false,
+                null);
 
-        }
+                NativeTypeMapping.AddTypeAlias("float8", typeof(Double));
+
+                NativeTypeMapping.AddType("numeric", NpgsqlDbType.Numeric, DbType.Decimal, false,
+                null);
+
+                NativeTypeMapping.AddTypeAlias("numeric", typeof(Decimal));
+
+                NativeTypeMapping.AddType("currency", NpgsqlDbType.Money, DbType.Currency, true,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToMoney));
+
+                NativeTypeMapping.AddType("date", NpgsqlDbType.Date, DbType.Date, true,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDate));
 
+                NativeTypeMapping.AddType("time", NpgsqlDbType.Time, DbType.Time, true,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToTime));
+
+                NativeTypeMapping.AddType("timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, true,
+                new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDateTime));
+
+                NativeTypeMapping.AddTypeAlias("timestamp", typeof(DateTime));
+
+                NativeTypeMapping.AddType("point", NpgsqlDbType.Point, DbType.Object, true,
+                new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPoint));
+
+                NativeTypeMapping.AddTypeAlias("point", typeof(NpgsqlPoint));
+                
+                NativeTypeMapping.AddType("box", NpgsqlDbType.Box, DbType.Object, true,
+                new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToBox));
+
+                NativeTypeMapping.AddTypeAlias("box", typeof(NpgsqlBox));
+                
+                NativeTypeMapping.AddType("lseg", NpgsqlDbType.LSeg, DbType.Object, true,
+                new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToLSeg));
+
+                NativeTypeMapping.AddTypeAlias("lseg", typeof(NpgsqlLSeg));
+
+                NativeTypeMapping.AddType("path", NpgsqlDbType.Path, DbType.Object, true,
+                new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPath));
+
+                NativeTypeMapping.AddTypeAlias("path", typeof(NpgsqlPath));
+
+                NativeTypeMapping.AddType("polygon", NpgsqlDbType.Polygon, DbType.Object, true,
+                new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPolygon));
+
+                NativeTypeMapping.AddTypeAlias("polygon", typeof(NpgsqlPolygon));
+
+                NativeTypeMapping.AddType("circle", NpgsqlDbType.Circle, DbType.Object, true,
+                new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToCircle));
+
+                NativeTypeMapping.AddTypeAlias("circle", typeof(NpgsqlCircle));
+            }
+        }
 
         ///<summary>
-        /// This method is responsible to convert the string received from the backend
-        /// to the corresponding NpgsqlType.
+        /// This method creates (or retrieves from cache) a mapping between type and OID 
+        /// of all natively supported postgresql data types.
+        /// This is needed as from one version to another, this mapping can be changed and
+        /// so we avoid hardcoding them.
         /// </summary>
-        ///
-        public static Object ConvertBackendStringToSystemType(Hashtable oidToNameMapping, String data, Int32 typeOid, Int32 typeModifier)
+        /// <returns>NpgsqlTypeMapping containing all known data types.  The mapping must be
+        /// cloned before it is modified because it is cached; changes made by one connection may
+        /// effect another connection.</returns>
+        public static NpgsqlBackendTypeMapping CreateAndLoadInitialTypesMapping(NpgsqlConnector conn)
         {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendStringToSystemType");
-            //[TODO] Find a way to eliminate this checking. It is just used at bootstrap time
-            // when connecting because we don't have yet loaded typeMapping. The switch below
-            // crashes with NullPointerReference when it can't find the typeOid.
-
-            if (!oidToNameMapping.ContainsKey(typeOid))
-                return data;
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping");
 
-            switch ((DbType)oidToNameMapping[typeOid])
+            // [TODO] Verify another way to get higher concurrency.
+            lock(CLASSNAME)
             {
-            case DbType.Binary:
-                return ConvertByteAToByteArray(data);
+                // Check the cache for an initial types map.
+                NpgsqlBackendTypeMapping oidToNameMapping = (NpgsqlBackendTypeMapping) BackendTypeMappingCache[conn.ServerVersion];
 
-            case DbType.Boolean:
-                return (data.ToLower() == "t" ? true : false);
+                if (oidToNameMapping != null)
+                {
+                    return oidToNameMapping;
+                }
 
-            case DbType.Single:
-                return Single.Parse(data, NumberFormatInfo.InvariantInfo);
+                // Not in cache, create a new one.
+                oidToNameMapping = new NpgsqlBackendTypeMapping();
 
-            case DbType.Double:
-                return Double.Parse(data, NumberFormatInfo.InvariantInfo);
+                // Create a list of all natively supported postgresql data types.
+                NpgsqlBackendTypeInfo[] TypeInfoList = new NpgsqlBackendTypeInfo[]
+                {
+                    new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType.Text, DbType.String, typeof(String),
+                        null),
 
-            case DbType.Int16:
-                return Int16.Parse(data);
-            case DbType.Int32:
-                return Int32.Parse(data);
+                    new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType.Text, DbType.String, typeof(String),
+                        null),
 
-            case DbType.Int64:
-                return Int64.Parse(data);
+                    new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType.Text, DbType.String, typeof(String),
+                        null),
 
-            case DbType.Decimal:
-                // Got this manipulation of typemodifier from jdbc driver - file AbstractJdbc1ResultSetMetaData.java.html method getColumnDisplaySize
-                {
-                    typeModifier -= 4;
-                    //Console.WriteLine("Numeric from server: {0} digitos.digitos {1}.{2}", data, (typeModifier >> 16) & 0xffff, typeModifier & 0xffff);
-                    return Decimal.Parse(data, NumberFormatInfo.InvariantInfo);
+                    new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType.Text, DbType.String, typeof(String),
+                        null),
 
-                }
+                    new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType.Text, DbType.String, typeof(String),
+                        null),
+                        
+                    new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType.Text, DbType.String, typeof(String),
+                        null),
 
-            case DbType.DateTime:
+                    new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType.Bytea, DbType.Binary, typeof(Byte[]),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBinary)),
 
-                // Get the date time parsed in all expected formats for timestamp.
-                return DateTime.ParseExact(data,
-                                           DateTimeFormats,
-                                           DateTimeFormatInfo.InvariantInfo,
-                                           DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
 
-            case DbType.Date:
-                return DateTime.ParseExact(data,
-                                           DateFormats,
-                                           DateTimeFormatInfo.InvariantInfo,
-                                           DateTimeStyles.AllowWhiteSpaces);
+                    new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType.Boolean, DbType.Boolean, typeof(Boolean),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBoolean)),
 
-            case DbType.Time:
 
-                return DateTime.ParseExact(data,
-                                           TimeFormats,
-                                           DateTimeFormatInfo.InvariantInfo,
-                                           DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
+                    new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType.Smallint, DbType.Int16, typeof(Int16),
+                        null),
 
-            case DbType.String:
-            case DbType.AnsiString:
-            case DbType.StringFixedLength:
-                return data;
-            default:
-                throw new InvalidCastException(String.Format(resman.GetString("Exception_TypeNotSupported"),  oidToNameMapping[typeOid]));
+                    new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType.Integer, DbType.Int32, typeof(Int32),
+                        null),
 
+                    new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64),
+                        null),
 
-            }
-        }
+                    new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64),
+                        null),
 
 
+                    new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType.Real, DbType.Single, typeof(Single),
+                        null),
 
-        ///<summary>
-        /// This method gets a type oid and return the equivalent
-        /// Npgsql type.
-        /// </summary>
-        ///
+                    new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType.Double, DbType.Double, typeof(Double),
+                        null),
 
-        public static Type GetSystemTypeFromTypeOid(Hashtable oidToNameMapping, Int32 typeOid)
-        {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSystemTypeFromTypeOid");
-            // This method gets a db type identifier and return the equivalent
-            // system type.
+                    new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof(Decimal),
+                        null),
 
-            //[TODO] Find a way to eliminate this checking. It is just used at bootstrap time
-            // when connecting because we don't have yet loaded typeMapping. The switch below
-            // crashes with NullPointerReference when it can't find the typeOid.
+                    new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Decimal, typeof(Decimal),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)),
 
 
+                    new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof(DateTime),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDate)),
 
-            if (!oidToNameMapping.ContainsKey(typeOid))
-                return Type.GetType("System.String");
+                    new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof(DateTime),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)),
+
+                    new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.Time, DbType.Time, typeof(DateTime),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)),
+
+                    new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)),
+
+                    new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime),
+                        new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)),
+
+
+                    new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType.Point, DbType.Object, typeof(NpgsqlPoint),
+                        new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPoint)),
+
+                    new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType.LSeg, DbType.Object, typeof(NpgsqlLSeg),
+                        new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToLSeg)),
+
+                    new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType.Path, DbType.Object, typeof(NpgsqlPath),
+                        new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPath)),
+
+                    new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType.Box, DbType.Object, typeof(NpgsqlBox),
+                        new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToBox)),
+
+                    new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType.Circle, DbType.Object, typeof(NpgsqlCircle),
+                        new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToCircle)),
+
+                    new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType.Polygon, DbType.Object, typeof(NpgsqlPolygon),
+                        new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPolygon)),
+                };
+
+                // Attempt to map each type info in the list to an OID on the backend and
+                // add each mapped type to the new type mapping object.
+                LoadTypesMappings(conn, oidToNameMapping, TypeInfoList);
+
+                // Add this mapping to the per-server-version cache so we don't have to
+                // do these expensive queries on every connection startup.
+                BackendTypeMappingCache.Add(conn.ServerVersion, oidToNameMapping);
+
+                return oidToNameMapping;
+            }
 
-            switch ((DbType)oidToNameMapping[typeOid])
-            {
-            case DbType.Binary:
-                return Type.GetType("System.Byte[]");
-            case DbType.Boolean:
-                return Type.GetType("System.Boolean");
-            case DbType.Int16:
-                return Type.GetType("System.Int16");
-            case DbType.Single:
-                return Type.GetType("System.Single");
-            case DbType.Double:
-                return Type.GetType("System.Double");
-            case DbType.Int32:
-                return Type.GetType("System.Int32");
-            case DbType.Int64:
-                return Type.GetType("System.Int64");
-            case DbType.Decimal:
-                return Type.GetType("System.Decimal");
-            case DbType.DateTime:
-            case DbType.Date:
-            case DbType.Time:
-                return Type.GetType("System.DateTime");
-            case DbType.String:
-            case DbType.AnsiString:
-            case DbType.StringFixedLength:
-                return Type.GetType("System.String");
-            default:
-                throw new InvalidCastException(String.Format(resman.GetString("Exception_TypeNotSupported"), oidToNameMapping[typeOid]));
 
+        }
+
+        /// <summary>
+        /// Attempt to map types by issuing a query against pg_type.
+        /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field
+        /// of each by querying pg_type.  If the mapping is found, the type info object is
+        /// updated (OID) and added to the provided NpgsqlTypeMapping object.
+        /// </summary>
+        /// <param name="conn">NpgsqlConnector to send query through.</param>
+        /// <param name="TypeMappings">Mapping object to add types too.</param>
+        /// <param name="TypeInfoList">List of types that need to have OID's mapped.</param>
+        public static void LoadTypesMappings(NpgsqlConnector conn, NpgsqlBackendTypeMapping TypeMappings, IList TypeInfoList)
+        {
+            StringBuilder       InList = new StringBuilder();
+            Hashtable           NameIndex = new Hashtable();
+
+            // Build a clause for the SELECT statement.
+            // Build a name->typeinfo mapping so we can match the results of the query
+            /// with the list of type objects efficiently.
+            foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList) {
+                NameIndex.Add(TypeInfo.Name, TypeInfo);
+                InList.AppendFormat("{0}'{1}'", ((InList.Length > 0) ? ", " : ""), TypeInfo.Name);
+            }
+
+            if (InList.Length == 0) {
+                return;
             }
 
+            NpgsqlCommand       command = new NpgsqlCommand("SELECT oid, typname FROM pg_type WHERE typname IN (" + InList.ToString() + ")", conn);
+            NpgsqlDataReader    dr = command.ExecuteReader();
+
+            while (dr.Read()) {
+                NpgsqlBackendTypeInfo TypeInfo = (NpgsqlBackendTypeInfo)NameIndex[dr[1].ToString()];
 
+                TypeInfo._OID = Convert.ToInt32(dr[0]);
+
+                TypeMappings.AddType(TypeInfo);
+            }
         }
+    }
 
+    /// <summary>
+    /// Delegate called to convert the given backend data to its native representation.
+    /// </summary>
+    internal delegate Object ConvertBackendToNativeHandler(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier);
+    /// <summary>
+    /// Delegate called to convert the given native data to its backand representation.
+    /// </summary>
+    internal delegate String ConvertNativeToBackendHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData);
 
-        ///<summary>
-        /// This method is responsible to send query to get the oid-to-name mapping.
-        /// This is needed as from one version to another, this mapping can be changed and
-        /// so we avoid hardcoding them.
+    /// <summary>
+    /// Represents a backend data type.
+    /// This class can be called upon to convert a backend field representation to a native object.
+    /// </summary>
+    internal class NpgsqlBackendTypeInfo
+    {
+        private event ConvertBackendToNativeHandler _ConvertBackendToNative;
+
+        internal Int32           _OID;
+        private String           _Name;
+        private NpgsqlDbType     _NpgsqlDbType;
+        private DbType           _DbType;
+        private Type             _Type;
+
+        /// <summary>
+        /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
         /// </summary>
-        public static Hashtable LoadTypesMapping(NpgsqlConnection conn)
+        /// <param name="OID">Type OID provided by the backend server.</param>
+        /// <param name="Name">Type name provided by the backend server.</param>
+        /// <param name="NpgsqlDbType">NpgsqlDbType</param>
+        /// <param name="Type">System type to convert fields of this type to.</param>
+        /// <param name="ConvertBackendToNative">Data conversion handler.</param>
+        public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type,
+                              ConvertBackendToNativeHandler ConvertBackendToNative)
         {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping");
+            _OID = OID;
+            _Name = Name;
+            _NpgsqlDbType = NpgsqlDbType;
+            _DbType = DbType;
+            _Type = Type;
+            _ConvertBackendToNative = ConvertBackendToNative;
+        }
 
-            // [TODO] Verify another way to get higher concurrency.
-            lock(typeof(NpgsqlTypesHelper))
-            {
-                Hashtable oidToNameMapping = (Hashtable) _oidToNameMappings[conn.ServerVersion];
+        /// <summary>
+        /// Type OID provided by the backend server.
+        /// </summary>
+        public Int32 OID
+        {
+          get { return _OID; }
+        }
 
-                if (oidToNameMapping != null)
-                {
-                    //conn.OidToNameMapping = oidToNameMapping;
-                    return oidToNameMapping;
+        /// <summary>
+        /// Type name provided by the backend server.
+        /// </summary>
+        public String Name
+        { get { return _Name; } }
+
+        /// <summary>
+        /// NpgsqlDbType.
+        /// </summary>
+        public NpgsqlDbType NpgsqlDbType
+        { get { return _NpgsqlDbType; } }
+
+        /// <summary>
+        /// NpgsqlDbType.
+        /// </summary>
+        public DbType DbType
+        { get { return _DbType; } }
+        
+        /// <summary>
+        /// System type to convert fields of this type to.
+        /// </summary>
+        public Type Type
+        { get { return _Type; } }
+
+        /// <summary>
+        /// Perform a data conversion from a backend representation to 
+        /// a native object.
+        /// </summary>
+        /// <param name="BackendData">Data sent from the backend.</param>
+        /// <param name="TypeModifier">Type modifier field sent from the backend.</param>
+        public Object ConvertToNative(String BackendData, Int16 TypeSize, Int32 TypeModifier)
+        {
+            if (_ConvertBackendToNative != null) {
+                return _ConvertBackendToNative(this, BackendData, TypeSize, TypeModifier);
+            } else {
+                try {
+                    return Convert.ChangeType(BackendData, Type, CultureInfo.InvariantCulture);
+                } catch {
+                    return BackendData;
                 }
+            }
+        }
+    }
 
+    /// <summary>
+    /// Represents a backend data type.
+    /// This class can be called upon to convert a native object to its backend field representation,
+    /// </summary>
+    internal class NpgsqlNativeTypeInfo
+    {
+        private event ConvertNativeToBackendHandler _ConvertNativeToBackend;
 
-                oidToNameMapping = new Hashtable();
-                //conn.OidToNameMapping = oidToNameMapping;
+        private String           _Name;
+        private NpgsqlDbType     _NpgsqlDbType;
+        private DbType           _DbType;
+        private Boolean          _Quote;
 
-                // Bootstrap value as the datareader below will use ConvertStringToNpgsqlType above.
-                //oidToNameMapping.Add(26, "oid");
+        /// <summary>
+        /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
+        /// </summary>
+        /// <param name="OID">Type OID provided by the backend server.</param>
+        /// <param name="Name">Type name provided by the backend server.</param>
+        /// <param name="NpgsqlDbType">NpgsqlDbType</param>
+        /// <param name="Type">System type to convert fields of this type to.</param>
+        /// <param name="ConvertBackendToNative">Data conversion handler.</param>
+        /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
+        public NpgsqlNativeTypeInfo(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote,
+                              ConvertNativeToBackendHandler ConvertNativeToBackend)
+        {
+            _Name = Name;
+            _NpgsqlDbType = NpgsqlDbType;
+            _DbType = DbType;
+            _Quote = Quote;
+            _ConvertNativeToBackend = ConvertNativeToBackend;
+        }
 
-                NpgsqlCommand command = new NpgsqlCommand("select oid, typname from pg_type where typname in ('bool', 'bytea', 'date', 'float4', 'float8', 'int2', 'int4', 'int8', 'numeric', 'text', 'time', 'timestamp', 'timestamptz', 'timetz');", conn);
+        /// <summary>
+        /// Type name provided by the backend server.
+        /// </summary>
+        public String Name
+        { get { return _Name; } }
 
-                NpgsqlDataReader dr = command.ExecuteReader();
+        /// <summary>
+        /// NpgsqlDbType.
+        /// </summary>
+        public NpgsqlDbType NpgsqlDbType
+        { get { return _NpgsqlDbType; } }
 
-                // Data was read. Clear the mapping from previous bootstrap value so we don't get
-                // exceptions trying to add duplicate key.
-                // oidToNameMapping.Clear();
+        /// <summary>
+        /// DbType.
+        /// </summary>
+        public DbType DbType
+        { get { return _DbType; } }
+        
+        
+        /// <summary>
+        /// Apply quoting.
+        /// </summary>
+        public Boolean Quote
+        { get { return _Quote; } }
 
-                while (dr.Read())
-                {
-                    // Add the key as a Int32 value so the switch in ConvertStringToNpgsqlType can use it
-                    // in the search. If don't, the key is added as string and the switch doesn't work.
-
-                    DbType type;
-                    String typeName = (String) dr[1];
-
-                    switch (typeName)
-                    {
-                    case "bool":
-                        type = DbType.Boolean;
-                        break;
-                    case "bytea":
-                        type = DbType.Binary;
-                        break;
-                    case "date":
-                        type = DbType.Date;
-                        break;
-                    case "float4":
-                        type = DbType.Single;
-                        break;
-                    case "float8":
-                        type = DbType.Double;
-                        break;
-                    case "int2":
-                        type = DbType.Int16;
-                        break;
-                    case "int4":
-                        type = DbType.Int32;
-                        break;
-                    case "int8":
-                        type = DbType.Int64;
-                        break;
-                    case "numeric":
-                        type = DbType.Decimal;
-                        break;
-                    case "time":
-                    case "timetz":
-                        type = DbType.Time;
-                        break;
-                    case "timestamp":
-                    case "timestamptz":
-                        type = DbType.DateTime;
-                        break;
-                    default:
-                        type = DbType.String; // Default dbtype of the oid. Unsupported types will be returned as String.
-                        break;
-                    }
-
-
-                    oidToNameMapping.Add(Int32.Parse((String)dr[0]), type);
-                }
+        /// <summary>
+        /// Perform a data conversion from a native object to
+        /// a backend representation.
+        /// DBNull will always be converted to "NULL".
+        /// </summary>
+        /// <param name="NativeData">Native .NET object to be converted.</param>
+        /// <param name="SuppressQuoting">Never add quotes (only applies to certain types).</param>
+        public String ConvertToBackend(Object NativeData, Boolean SuppressQuoting)
+        {
+            if (NativeData == DBNull.Value) {
+                return "NULL";
+            } else if (_ConvertNativeToBackend != null) {
+                return QuoteString(! SuppressQuoting, _ConvertNativeToBackend(this, NativeData));
+            } else {
+                return QuoteString(! SuppressQuoting, (String)Convert.ChangeType(NativeData, typeof(String), CultureInfo.InvariantCulture));
+            }
+        }
 
-                _oidToNameMappings.Add(conn.ServerVersion, oidToNameMapping);
-                return oidToNameMapping;
+        private static String QuoteString(Boolean Quote, String S)
+        {
+            if (Quote) {
+                return String.Format("'{0}'", S);
+            } else {
+                return S;
             }
+        }
+    }
+
+    /// <summary>
+    /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it.
+    /// </summary>
+    internal class NpgsqlBackendTypeMapping
+    {
+        private Hashtable       OIDIndex;
+        private Hashtable       NameIndex;
 
+        /// <summary>
+        /// Construct an empty mapping.
+        /// </summary>
+        public NpgsqlBackendTypeMapping()
+        {
+            OIDIndex = new Hashtable();
+            NameIndex = new Hashtable();
+        }
 
+        /// <summary>
+        /// Copy constuctor.
+        /// </summary>
+        private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other)
+        {
+            OIDIndex = (Hashtable)Other.OIDIndex.Clone();
+            NameIndex = (Hashtable)Other.NameIndex.Clone();
         }
 
+        /// <summary>
+        /// Add the given NpgsqlBackendTypeInfo to this mapping.
+        /// </summary>
+        public void AddType(NpgsqlBackendTypeInfo T)
+        {
+            if (OIDIndex.Contains(T.OID)) {
+                throw new Exception("Type already mapped");
+            }
 
+            OIDIndex[T.OID] = T;
+            NameIndex[T.Name] = T;
+        }
 
-        private static Byte[] ConvertByteAToByteArray(String byteA)
+        /// <summary>
+        /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping.
+        /// </summary>
+        /// <param name="OID">Type OID provided by the backend server.</param>
+        /// <param name="Name">Type name provided by the backend server.</param>
+        /// <param name="NpgsqlDbType">NpgsqlDbType</param>
+        /// <param name="Type">System type to convert fields of this type to.</param>
+        /// <param name="ConvertBackendToNative">Data conversion handler.</param>
+        public void AddType(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type,
+                            ConvertBackendToNativeHandler BackendConvert)
         {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertByteAToByteArray");
-            Int32 octalValue = 0;
-            Int32 byteAPosition = 0;
+            AddType(new NpgsqlBackendTypeInfo(OID, Name, NpgsqlDbType, DbType, Type, BackendConvert));
+        }
 
-            Int32 byteAStringLength = byteA.Length;
+        /// <summary>
+        /// Get the number of type infos held.
+        /// </summary>
+        public Int32 Count
+        { get { return NameIndex.Count; } }
 
-            MemoryStream ms = new MemoryStream();
+        /// <summary>
+        /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found.
+        /// </summary>
+        public NpgsqlBackendTypeInfo this [Int32 OID]
+        {
+            get
+            {
+                return (NpgsqlBackendTypeInfo)OIDIndex[OID];
+            }
+        }
 
-            while (byteAPosition < byteAStringLength)
+        /// <summary>
+        /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found.
+        /// </summary>
+        public NpgsqlBackendTypeInfo this [String Name]
+        {
+            get
             {
+                return (NpgsqlBackendTypeInfo)NameIndex[Name];
+            }
+        }
 
+        /// <summary>
+        /// Make a shallow copy of this type mapping.
+        /// </summary>
+        public NpgsqlBackendTypeMapping Clone()
+        {
+            return new NpgsqlBackendTypeMapping(this);
+        }
+
+        /// <summary>
+        /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping.
+        /// </summary>
+        public Boolean ContainsOID(Int32 OID)
+        {
+            return OIDIndex.ContainsKey(OID);
+        }
+
+        /// <summary>
+        /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping.
+        /// </summary>
+        public Boolean ContainsName(String Name)
+        {
+            return NameIndex.ContainsKey(Name);
+        }
+    }
 
-                // The IsDigit is necessary in case we receive a \ as the octal value and not
-                // as the indicator of a following octal value in decimal format.
-                // i.e.: \201\301P\A
-                if (byteA[byteAPosition] == '\\')
-
-                    if (byteAPosition + 1 == byteAStringLength)
-                    {
-                        octalValue = '\\';
-                        byteAPosition++;
-                    }
-                    else if (Char.IsDigit(byteA[byteAPosition + 1]))
-                    {
-                        octalValue = (Byte.Parse(byteA[byteAPosition + 1].ToString()) << 6);
-                        octalValue |= (Byte.Parse(byteA[byteAPosition + 2].ToString()) << 3);
-                        octalValue |= Byte.Parse(byteA[byteAPosition + 3].ToString());
-                        byteAPosition += 4;
-
-                    }
-                    else
-                    {
-                        octalValue = '\\';
-                        byteAPosition += 2;
-                    }
-
-
-                else
-                {
-                    octalValue = (Byte)byteA[byteAPosition];
-                    byteAPosition++;
-                }
 
 
-                ms.WriteByte((Byte)octalValue);
+    /// <summary>
+    /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it.
+    /// </summary>
+    internal class NpgsqlNativeTypeMapping
+    {
+        private Hashtable       NameIndex;
+        private Hashtable       NpgsqlDbTypeIndex;
+        private Hashtable       DbTypeIndex;
+        private Hashtable       TypeIndex;
 
+        /// <summary>
+        /// Construct an empty mapping.
+        /// </summary>
+        public NpgsqlNativeTypeMapping()
+        {
+            NameIndex = new Hashtable();
+            NpgsqlDbTypeIndex = new Hashtable();
+            DbTypeIndex = new Hashtable();
+            TypeIndex = new Hashtable();
+        }
+
+        /// <summary>
+        /// Add the given NpgsqlNativeTypeInfo to this mapping.
+        /// </summary>
+        public void AddType(NpgsqlNativeTypeInfo T)
+        {
+            if (NameIndex.Contains(T.Name)) {
+                throw new Exception("Type already mapped");
             }
 
-            return ms.ToArray();
+            NameIndex[T.Name] = T;
+            NpgsqlDbTypeIndex[T.NpgsqlDbType] = T;
+            DbTypeIndex[T.DbType] = T;
+        }
 
+        /// <summary>
+        /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping.
+        /// </summary>
+        /// <param name="OID">Type OID provided by the backend server.</param>
+        /// <param name="Name">Type name provided by the backend server.</param>
+        /// <param name="NpgsqlDbType">NpgsqlDbType</param>
+        /// <param name="ConvertBackendToNative">Data conversion handler.</param>
+        /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
+        public void AddType(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote,
+                            ConvertNativeToBackendHandler NativeConvert)
+        {
+            AddType(new NpgsqlNativeTypeInfo(Name, NpgsqlDbType, DbType, Quote, NativeConvert));
+        }
 
+        public void AddNpgsqlDbTypeAlias(String Name, NpgsqlDbType NpgsqlDbType)
+        {
+            if (NpgsqlDbTypeIndex.Contains(NpgsqlDbType)) {
+                throw new Exception("NpgsqlDbType already aliased");
+            }
+
+            NpgsqlDbTypeIndex[NpgsqlDbType] = NameIndex[Name];
+        }
+        
+        public void AddDbTypeAlias(String Name, DbType DbType)
+        {
+            if (DbTypeIndex.Contains(DbType)) {
+                throw new Exception("NpgsqlDbType already aliased");
+            }
+
+            DbTypeIndex[DbType] = NameIndex[Name];
         }
 
-        private static String ConvertByteArrayToBytea(Byte[] byteArray)
+        public void AddTypeAlias(String Name, Type Type)
         {
-            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertByteArrayToBytea");
-            int len = byteArray.Length;
-            char[] res = new char[len * 5];
-            for (int i=0, o=0; i<len; ++i, o += 5)
+            if (TypeIndex.Contains(Type)) {
+                throw new Exception("Type already aliased");
+            }
+
+            TypeIndex[Type] = NameIndex[Name];
+        }
+
+        /// <summary>
+        /// Get the number of type infos held.
+        /// </summary>
+        public Int32 Count
+        { get { return NameIndex.Count; } }
+
+        /// <summary>
+        /// Retrieve the NpgsqlNativeTypeInfo with the given backend type name, or null if none found.
+        /// </summary>
+        public NpgsqlNativeTypeInfo this [String Name]
+        {
+            get
+            {
+                return (NpgsqlNativeTypeInfo)NameIndex[Name];
+            }
+        }
+
+        /// <summary>
+        /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType, or null if none found.
+        /// </summary>
+        public NpgsqlNativeTypeInfo this [NpgsqlDbType NpgsqlDbType]
+        {
+            get
+            {
+                return (NpgsqlNativeTypeInfo)NpgsqlDbTypeIndex[NpgsqlDbType];
+            }
+        }
+        
+        /// <summary>
+        /// Retrieve the NpgsqlNativeTypeInfo with the given DbType, or null if none found.
+        /// </summary>
+        public NpgsqlNativeTypeInfo this [DbType DbType]
+        {
+            get
+            {
+                return (NpgsqlNativeTypeInfo)DbTypeIndex[DbType];
+            }
+        }
+        
+        
+
+        /// <summary>
+        /// Retrieve the NpgsqlNativeTypeInfo with the given Type, or null if none found.
+        /// </summary>
+        public NpgsqlNativeTypeInfo this [Type Type]
+        {
+            get
             {
-                byte item = byteArray[i];
-                res[o] = res[o + 1] = '\\';
-                res[o + 2] = (char)('0' + (7 & (item >> 6)));
-                res[o + 3] = (char)('0' + (7 & (item >> 3)));
-                res[o + 4] = (char)('0' + (7 & item));
+                return (NpgsqlNativeTypeInfo)TypeIndex[Type];            
             }
-            return new String(res);
+        }
 
+        /// <summary>
+        /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping.
+        /// </summary>
+        public Boolean ContainsName(String Name)
+        {
+            return NameIndex.ContainsKey(Name);
+        }
+
+        /// <summary>
+        /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping.
+        /// </summary>
+        public Boolean ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType)
+        {
+            return NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType);
+        }
+
+        /// <summary>
+        /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping.
+        /// </summary>
+        public Boolean ContainsType(Type Type)
+        {
+            return TypeIndex.ContainsKey(Type);
         }
     }
 }

BIN
mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.resources


+ 499 - 45
mcs/class/Npgsql/Test/CommandTests.cs

@@ -29,6 +29,7 @@ using System.Data;
 using System.Globalization;
 using NpgsqlTypes;
 
+
 namespace NpgsqlTests
 {
 	
@@ -36,7 +37,7 @@ namespace NpgsqlTests
 	public class CommandTests
 	{
 		private NpgsqlConnection 	_conn = null;
-		private String 						_connString = "Server=localhost;User ID=npgsql_tests;Password=npgsql_tests;Database=npgsql_tests;SSL=yes;maxpoolsize=2;";
+		private String 						_connString = "Server=localhost;User ID=npgsql_tests;Password=npgsql_tests;Database=npgsql_tests;maxpoolsize=2";
 		
 		[SetUp]
 		protected void SetUp()
@@ -68,14 +69,14 @@ namespace NpgsqlTests
 			
 			// Get by indexers.
 			
-			Assertion.AssertEquals("ParametersGetName", ":Parameter1", command.Parameters[":Parameter1"].ParameterName);
-			Assertion.AssertEquals("ParametersGetName", ":Parameter2", command.Parameters[":Parameter2"].ParameterName);
-			Assertion.AssertEquals("ParametersGetName", ":Parameter3", command.Parameters[":Parameter3"].ParameterName);
+			Assert.AreEqual(":Parameter1", command.Parameters[":Parameter1"].ParameterName);
+			Assert.AreEqual(":Parameter2", command.Parameters[":Parameter2"].ParameterName);
+			Assert.AreEqual(":Parameter3", command.Parameters[":Parameter3"].ParameterName);
 						                 
 
-			Assertion.AssertEquals("ParametersGetName", ":Parameter1", command.Parameters[0].ParameterName);
-			Assertion.AssertEquals("ParametersGetName", ":Parameter2", command.Parameters[1].ParameterName);
-			Assertion.AssertEquals("ParametersGetName", ":Parameter3", command.Parameters[2].ParameterName);						             
+			Assert.AreEqual(":Parameter1", command.Parameters[0].ParameterName);
+			Assert.AreEqual(":Parameter2", command.Parameters[1].ParameterName);
+			Assert.AreEqual(":Parameter3", command.Parameters[2].ParameterName);						             
 			
 			
 			
@@ -123,7 +124,7 @@ namespace NpgsqlTests
 			
 			Object result = command.ExecuteScalar();
 			
-			Assertion.AssertEquals(5, result);
+			Assert.AreEqual(5, result);
 			//reader.FieldCount
 			
 		}
@@ -138,7 +139,7 @@ namespace NpgsqlTests
 						
 			Object result = command.ExecuteScalar();
 			
-			Assertion.AssertEquals(5, result);
+			Assert.AreEqual(5, result);
 			//reader.FieldCount
 			
 		}
@@ -155,7 +156,7 @@ namespace NpgsqlTests
 			command.Prepare();
 			Object result = command.ExecuteScalar();
 			
-			Assertion.AssertEquals(5, result);
+			Assert.AreEqual(5, result);
 			//reader.FieldCount
 			
 		}
@@ -174,11 +175,31 @@ namespace NpgsqlTests
 						
 			Int64 result = (Int64) command.ExecuteScalar();
 			
-			Assertion.AssertEquals(1, result);
+			Assert.AreEqual(1, result);
 			
 			
 		}
 		
+		[Test]
+		public void FunctionCallWithParametersReturnSingleValueNpgsqlDbType()
+		{
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("funcC(:a)", _conn);
+			command.CommandType = CommandType.StoredProcedure;
+			
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Integer));
+						
+			command.Parameters[0].Value = 4;
+						
+			Int64 result = (Int64) command.ExecuteScalar();
+			
+			Assert.AreEqual(1, result);
+			
+		}
+		
+		
+		
 		
 		[Test]
 		public void FunctionCallWithParametersPrepareReturnSingleValue()
@@ -191,7 +212,7 @@ namespace NpgsqlTests
 			
 			command.Parameters.Add(new NpgsqlParameter("a", DbType.Int32));
 			
-			Assertion.AssertEquals(1, command.Parameters.Count);
+			Assert.AreEqual(1, command.Parameters.Count);
 			command.Prepare();
 			
 			
@@ -199,11 +220,36 @@ namespace NpgsqlTests
 						
 			Int64 result = (Int64) command.ExecuteScalar();
 			
-			Assertion.AssertEquals(1, result);
+			Assert.AreEqual(1, result);
 			
 			
 		}
 		
+		[Test]
+		public void FunctionCallWithParametersPrepareReturnSingleValueNpgsqlDbType()
+		{
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("funcC(:a)", _conn);
+			command.CommandType = CommandType.StoredProcedure;
+			
+			
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Integer));
+			
+			Assert.AreEqual(1, command.Parameters.Count);
+			command.Prepare();
+			
+			
+			command.Parameters[0].Value = 4;
+						
+			Int64 result = (Int64) command.ExecuteScalar();
+			
+			Assert.AreEqual(1, result);
+			
+			
+		}
+		
+		
 		[Test]
 		public void FunctionCallReturnResultSet()
 		{
@@ -244,7 +290,7 @@ namespace NpgsqlTests
 				i++;
 			}
 			
-			Assertion.AssertEquals(3, i);
+			Assert.AreEqual(3, i);
 			
 			
 			i = 0;
@@ -258,7 +304,7 @@ namespace NpgsqlTests
 				i++;
 			}
 			
-			Assertion.AssertEquals(1, i);
+			Assert.AreEqual(1, i);
 			
 			command.CommandText = "close te;";
 			
@@ -296,9 +342,35 @@ namespace NpgsqlTests
 			command.Parameters.Add(new NpgsqlParameter("a", DbType.Int32));
 			command.Parameters.Add(new NpgsqlParameter("b", DbType.Int64));
 			
-			Assertion.AssertEquals(2, command.Parameters.Count);
+			Assert.AreEqual(2, command.Parameters.Count);
+			
+			Assert.AreEqual(DbType.Int32, command.Parameters[0].DbType);
+			
+			command.Prepare();
+			
+			command.Parameters[0].Value = 3;
+			command.Parameters[1].Value = 5;
+			
+			NpgsqlDataReader dr = command.ExecuteReader();
+			
+			
+			
+			
+		}
+		
+		[Test]
+		public void PreparedStatementWithParametersNpgsqlDbType()
+		{
+			_conn.Open();
 			
-			Assertion.AssertEquals(DbType.Int32, command.Parameters[0].DbType);
+			NpgsqlCommand command = new NpgsqlCommand("select * from tablea where field_int4 = :a and field_int8 = :b;", _conn);
+			
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Integer));
+			command.Parameters.Add(new NpgsqlParameter("b", NpgsqlDbType.Bigint));
+			
+			Assert.AreEqual(2, command.Parameters.Count);
+			
+			Assert.AreEqual(DbType.Int32, command.Parameters[0].DbType);
 			
 			command.Prepare();
 			
@@ -312,6 +384,7 @@ namespace NpgsqlTests
 			
 		}
 		
+		
 		[Test]
 		[ExpectedException(typeof(InvalidOperationException))]
 		public void ListenNotifySupport()
@@ -348,7 +421,7 @@ namespace NpgsqlTests
 			DateTime d = (DateTime)command.ExecuteScalar();
 			
 			
-			Assertion.AssertEquals("2002-02-02 09:00:23Z", d.ToString("u"));
+			Assert.AreEqual("2002-02-02 09:00:23Z", d.ToString("u"));
 			
 			DateTimeFormatInfo culture = new DateTimeFormatInfo();
 			culture.TimeSeparator = ":";
@@ -362,6 +435,32 @@ namespace NpgsqlTests
 			
 		}
 		
+		
+		[Test]
+		public void DateTimeSupportNpgsqlDbType()
+		{
+			_conn.Open();
+			
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_timestamp from tableb where field_serial = 2;", _conn);
+			
+			DateTime d = (DateTime)command.ExecuteScalar();
+			
+			
+			Assert.AreEqual("2002-02-02 09:00:23Z", d.ToString("u"));
+			
+			DateTimeFormatInfo culture = new DateTimeFormatInfo();
+			culture.TimeSeparator = ":";
+			DateTime dt = System.DateTime.Parse("2004-06-04 09:48:00", culture);
+
+			command.CommandText = "insert into tableb(field_timestamp) values (:a);delete from tableb where field_serial > 4;";
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Timestamp));
+			command.Parameters[0].Value = dt;
+			
+			command.ExecuteScalar();
+			
+		}
+		
 		[Test]
 		public void DateSupport()
 		{
@@ -372,7 +471,7 @@ namespace NpgsqlTests
 			DateTime d = (DateTime)command.ExecuteScalar();
 			
 			
-			Assertion.AssertEquals("2002-03-04", d.ToString("yyyy-MM-dd"));
+			Assert.AreEqual("2002-03-04", d.ToString("yyyy-MM-dd"));
 			
 		}
 		
@@ -386,7 +485,7 @@ namespace NpgsqlTests
 			DateTime d = (DateTime)command.ExecuteScalar();
 			
 			
-			Assertion.AssertEquals("10:03:45.345", d.ToString("HH:mm:ss.fff"));
+			Assert.AreEqual("10:03:45.345", d.ToString("HH:mm:ss.fff"));
 			
 		}
 		
@@ -403,7 +502,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select * from tableb where field_numeric = :a";
 			
@@ -419,13 +518,50 @@ namespace NpgsqlTests
 			command.ExecuteNonQuery();
 			
 			
-			Assertion.AssertEquals(7.4M, result);
+			Assert.AreEqual(7.4000000M, result);
 			
 			
 			
 			
 		}
 		
+		[Test]
+		public void NumericSupportNpgsqlDbType()
+		{
+			_conn.Open();
+			
+			
+			NpgsqlCommand command = new NpgsqlCommand("insert into tableb(field_numeric) values (:a)", _conn);
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Numeric));
+			
+			command.Parameters[0].Value = 7.4M;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+			Assert.AreEqual(1, rowsAdded);
+			
+			command.CommandText = "select * from tableb where field_numeric = :a";
+			
+			
+			NpgsqlDataReader dr = command.ExecuteReader();
+			dr.Read();
+			
+			Decimal result = dr.GetDecimal(3);
+			
+			
+			command.CommandText = "delete from tableb where field_serial = (select max(field_serial) from tableb) and field_serial != 3;";
+			command.Parameters.Clear();
+			command.ExecuteNonQuery();
+			
+			
+			Assert.AreEqual(7.4000000M, result);
+			
+			
+			
+			
+		}
+		
+		
 		[Test]
 		public void InsertSingleValue()
 		{
@@ -439,7 +575,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select * from tabled where field_float4 = :a";
 			
@@ -455,7 +591,41 @@ namespace NpgsqlTests
 			command.ExecuteNonQuery();
 			
 			
-			Assertion.AssertEquals(7.4F, result);
+			Assert.AreEqual(7.4F, result);
+			
+		}
+		
+		
+		[Test]
+		public void InsertSingleValueNpgsqlDbType()
+		{
+		  _conn.Open();
+			
+			
+			NpgsqlCommand command = new NpgsqlCommand("insert into tabled(field_float4) values (:a)", _conn);
+			command.Parameters.Add(new NpgsqlParameter(":a", NpgsqlDbType.Real));
+			
+			command.Parameters[0].Value = 7.4F;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+			Assert.AreEqual(1, rowsAdded);
+			
+			command.CommandText = "select * from tabled where field_float4 = :a";
+			
+			
+			NpgsqlDataReader dr = command.ExecuteReader();
+			dr.Read();
+			
+			Single result = dr.GetFloat(1);
+			
+			
+			command.CommandText = "delete from tabled where field_serial > 2;";
+			command.Parameters.Clear();
+			command.ExecuteNonQuery();
+			
+			
+			Assert.AreEqual(7.4F, result);
 			
 		}
 		
@@ -472,7 +642,41 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
+			
+			command.CommandText = "select * from tabled where field_float8 = :a";
+			
+			
+			NpgsqlDataReader dr = command.ExecuteReader();
+			dr.Read();
+			
+			Double result = dr.GetDouble(2);
+			
+			
+			command.CommandText = "delete from tabled where field_serial > 2;";
+			command.Parameters.Clear();
+			//command.ExecuteNonQuery();
+			
+			
+			Assert.AreEqual(7.4D, result);
+			
+		}
+		
+		
+		[Test]
+		public void InsertDoubleValueNpgsqlDbType()
+		{
+		  _conn.Open();
+			
+			
+			NpgsqlCommand command = new NpgsqlCommand("insert into tabled(field_float8) values (:a)", _conn);
+			command.Parameters.Add(new NpgsqlParameter(":a", NpgsqlDbType.Double));
+			
+			command.Parameters[0].Value = 7.4D;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select * from tabled where field_float8 = :a";
 			
@@ -488,10 +692,11 @@ namespace NpgsqlTests
 			//command.ExecuteNonQuery();
 			
 			
-			Assertion.AssertEquals(7.4D, result);
+			Assert.AreEqual(7.4D, result);
 			
 		}
 		
+		
 		[Test]
 		public void NegativeNumericSupport()
 		{
@@ -506,10 +711,11 @@ namespace NpgsqlTests
 			
 			Decimal result = dr.GetDecimal(3);
 			
-			Assertion.AssertEquals(-4.3M, result);
+			Assert.AreEqual(-4.3000000M, result);
 			
 		}
 		
+		
 		[Test]
 		public void PrecisionScaleNumericSupport()
 		{
@@ -524,9 +730,9 @@ namespace NpgsqlTests
 			
 			Decimal result = dr.GetDecimal(3);
 			
-			Assertion.AssertEquals(-4.3M, (Decimal)result);
-			//Assertion.AssertEquals(11, result.Precision);
-			//Assertion.AssertEquals(7, result.Scale);
+			Assert.AreEqual(-4.3000000M, (Decimal)result);
+			//Assert.AreEqual(11, result.Precision);
+			//Assert.AreEqual(7, result.Scale);
 			
 		}
 		
@@ -543,7 +749,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select count(*) from tablea where field_text is null";
 			command.Parameters.Clear();
@@ -553,11 +759,42 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tablea where field_serial = (select max(field_serial) from tablea) and field_serial != 4;";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(4, result);
+			Assert.AreEqual(4, result);
 			
 			
 			
 		}
+		
+		[Test]
+		public void InsertNullStringNpgsqlDbType()
+		{
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("insert into tablea(field_text) values (:a)", _conn);
+			
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Text));
+			
+			command.Parameters[0].Value = DBNull.Value;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+			Assert.AreEqual(1, rowsAdded);
+			
+			command.CommandText = "select count(*) from tablea where field_text is null";
+			command.Parameters.Clear();
+			
+			Int64 result = (Int64)command.ExecuteScalar();
+			
+			command.CommandText = "delete from tablea where field_serial = (select max(field_serial) from tablea) and field_serial != 4;";
+			command.ExecuteNonQuery();
+			
+			Assert.AreEqual(4, result);
+			
+			
+			
+		}
+		
+		
 	
 		[Test]
 		public void InsertNullDateTime()
@@ -572,7 +809,37 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
+			
+			command.CommandText = "select count(*) from tableb where field_timestamp is null";
+			command.Parameters.Clear();
+			
+			Object result = command.ExecuteScalar();
+			
+			command.CommandText = "delete from tableb where field_serial = (select max(field_serial) from tableb) and field_serial != 3;";
+			command.ExecuteNonQuery();
+			
+			Assert.AreEqual(4, result);
+			
+			
+			
+		}
+		
+		
+		[Test]
+		public void InsertNullDateTimeNpgsqlDbType()
+		{
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("insert into tableb(field_timestamp) values (:a)", _conn);
+			
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Timestamp));
+			
+			command.Parameters[0].Value = DBNull.Value;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select count(*) from tableb where field_timestamp is null";
 			command.Parameters.Clear();
@@ -582,13 +849,14 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tableb where field_serial = (select max(field_serial) from tableb) and field_serial != 3;";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(4, result);
+			Assert.AreEqual(4, result);
 			
 			
 			
 		}
 		
 		
+		
 		[Test]
 		public void InsertNullInt16()
 		{
@@ -603,7 +871,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select count(*) from tableb where field_int2 is null";
 			command.Parameters.Clear();
@@ -613,7 +881,37 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tableb where field_serial = (select max(field_serial) from tableb);";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(4, result);
+			Assert.AreEqual(4, result);
+			
+			
+		}
+		
+		
+		[Test]
+		public void InsertNullInt16NpgsqlDbType()
+		{
+			_conn.Open();
+			
+			
+			NpgsqlCommand command = new NpgsqlCommand("insert into tableb(field_int2) values (:a)", _conn);
+			
+			command.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Smallint));
+			
+			command.Parameters[0].Value = DBNull.Value;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+			Assert.AreEqual(1, rowsAdded);
+			
+			command.CommandText = "select count(*) from tableb where field_int2 is null";
+			command.Parameters.Clear();
+			
+			Object result = command.ExecuteScalar(); // The missed cast is needed as Server7.2 returns Int32 and Server7.3+ returns Int64
+			
+			command.CommandText = "delete from tableb where field_serial = (select max(field_serial) from tableb);";
+			command.ExecuteNonQuery();
+			
+			Assert.AreEqual(4, result);
 			
 			
 		}
@@ -633,7 +931,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select count(*) from tablea where field_int4 is null";
 			command.Parameters.Clear();
@@ -643,7 +941,7 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tablea where field_serial = (select max(field_serial) from tablea);";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(5, result);
+			Assert.AreEqual(5, result);
 			
 		}
 		
@@ -662,7 +960,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select count(*) from tableb where field_numeric is null";
 			command.Parameters.Clear();
@@ -672,7 +970,7 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tableb where field_serial = (select max(field_serial) from tableb);";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(3, result);
+			Assert.AreEqual(3, result);
 			
 		}
 		
@@ -690,7 +988,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = "select count(*) from tablea where field_bool is null";
 			command.Parameters.Clear();
@@ -700,7 +998,7 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tablea where field_serial = (select max(field_serial) from tablea);";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(5, result);
+			Assert.AreEqual(5, result);
 			
 		}
         
@@ -717,7 +1015,7 @@ namespace NpgsqlTests
 			
 			Int32 rowsAdded = command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, rowsAdded);
+			Assert.AreEqual(1, rowsAdded);
 			
 			command.CommandText = String.Format("select count(*) from tablea where field_text = '{0}'", command.Parameters[0].Value);
 			command.Parameters.Clear();
@@ -727,7 +1025,7 @@ namespace NpgsqlTests
 			command.CommandText = "delete from tablea where field_serial = (select max(field_serial) from tablea);";
 			command.ExecuteNonQuery();
 			
-			Assertion.AssertEquals(1, result);
+			Assert.AreEqual(1, result);
 			
 		}
 		
@@ -749,7 +1047,7 @@ namespace NpgsqlTests
             command.ExecuteScalar();
             
             
-            Assertion.AssertEquals(6, result);
+            Assert.AreEqual(6, result);
             
             
 		}
@@ -821,6 +1119,162 @@ namespace NpgsqlTests
             
         }
         
+        
+        [Test]
+		public void TestParameterReplace()
+		{
+			_conn.Open();
+			
+			String sql = @"select * from tablea where 
+			field_serial = :a
+			";
+			
+			
+			NpgsqlCommand command = new NpgsqlCommand(sql, _conn);
+			
+			command.Parameters.Add(new NpgsqlParameter("a", DbType.Int32));
+			
+			command.Parameters[0].Value = 2;
+			
+			Int32 rowsAdded = command.ExecuteNonQuery();
+			
+		}
+		
+		[Test]
+		public void TestPointSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_point from tablee where field_serial = 1", _conn);
+			
+			NpgsqlPoint p = (NpgsqlPoint) command.ExecuteScalar();
+			
+			Assert.AreEqual(4, p.X);
+			Assert.AreEqual(3, p.Y);
+		}
+		
+		
+		[Test]
+		public void TestBoxSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_box from tablee where field_serial = 2", _conn);
+			
+			NpgsqlBox box = (NpgsqlBox) command.ExecuteScalar();
+			
+			Assert.AreEqual(5, box.UpperRight.X);
+			Assert.AreEqual(4, box.UpperRight.Y);
+			Assert.AreEqual(4, box.LowerLeft.X);
+			Assert.AreEqual(3, box.LowerLeft.Y);
+			
+			
+		}
+		
+		[Test]
+		public void TestLSegSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_lseg from tablee where field_serial = 3", _conn);
+			
+			NpgsqlLSeg lseg = (NpgsqlLSeg) command.ExecuteScalar();
+			
+			Assert.AreEqual(4, lseg.Start.X);
+			Assert.AreEqual(3, lseg.Start.Y);
+			Assert.AreEqual(5, lseg.End.X);
+			Assert.AreEqual(4, lseg.End.Y);
+			
+			
+		}
+		
+		[Test]
+		public void TestClosedPathSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_path from tablee where field_serial = 4", _conn);
+			
+			NpgsqlPath path = (NpgsqlPath) command.ExecuteScalar();
+			
+			Assert.AreEqual(false, path.Open);
+			Assert.AreEqual(2, path.Count);
+			Assert.AreEqual(4, path[0].X);
+			Assert.AreEqual(3, path[0].Y);
+			Assert.AreEqual(5, path[1].X);
+			Assert.AreEqual(4, path[1].Y);
+			
+			
+		}
+		
+		[Test]
+		public void TestOpenPathSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_path from tablee where field_serial = 5", _conn);
+			
+			NpgsqlPath path = (NpgsqlPath) command.ExecuteScalar();
+			
+			Assert.AreEqual(true, path.Open);
+			Assert.AreEqual(2, path.Count);
+			Assert.AreEqual(4, path[0].X);
+			Assert.AreEqual(3, path[0].Y);
+			Assert.AreEqual(5, path[1].X);
+			Assert.AreEqual(4, path[1].Y);
+			
+			
+		}
+		
+		
+		
+		[Test]
+		public void TestPolygonSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_polygon from tablee where field_serial = 6", _conn);
+			
+			NpgsqlPolygon polygon = (NpgsqlPolygon) command.ExecuteScalar();
+			
+			Assert.AreEqual(2, polygon.Count);
+			Assert.AreEqual(4, polygon[0].X);
+			Assert.AreEqual(3, polygon[0].Y);
+			Assert.AreEqual(5, polygon[1].X);
+			Assert.AreEqual(4, polygon[1].Y);
+			
+			
+		}
+		
+		
+		[Test]
+		public void TestCircleSupport()
+		{
+			
+			_conn.Open();
+			
+			NpgsqlCommand command = new NpgsqlCommand("select field_circle from tablee where field_serial = 7", _conn);
+			
+			NpgsqlCircle circle = (NpgsqlCircle) command.ExecuteScalar();
+			
+			Assert.AreEqual(4, circle.Center.X);
+			Assert.AreEqual(3, circle.Center.Y);
+			Assert.AreEqual(5, circle.Radius);
+			
+			
+			
+		}
+		
+		
+		
+		
+		
 		
 	}
 }

+ 2 - 2
mcs/class/Npgsql/Test/ConnectionTests.cs

@@ -59,7 +59,7 @@ namespace NpgsqlTests
 		{
 			try{
 				_conn.Open();
-				//Assertion.AssertEquals("ConnectionOpen", ConnectionState.Open, _conn.State);
+				//Assert.AreEqual("ConnectionOpen", ConnectionState.Open, _conn.State);
 			} catch (Exception e)
 			{
 				Console.WriteLine(e.ToString());
@@ -79,7 +79,7 @@ namespace NpgsqlTests
 			
 			String result = (String)command.ExecuteScalar();
 			
-			Assertion.AssertEquals("template1", result);
+			Assert.AreEqual("template1", result);
 				
 		}
 		

+ 9 - 8
mcs/class/Npgsql/Test/DataAdapterTests.cs

@@ -109,12 +109,13 @@ namespace NpgsqlTests
 			NpgsqlDataReader dr2 = new NpgsqlCommand("select * from tableb where field_serial > 4", _conn).ExecuteReader();
 			dr2.Read();
 			
-			Assertion.AssertEquals(4, dr2[1]);
-			Assertion.AssertEquals(7.3M, dr2[3]);
 			
+			Assert.AreEqual(4, dr2[1]);
+			Assert.AreEqual(7.3000000M, dr2[3]);
 			
 			new NpgsqlCommand("delete from tableb where field_serial > 4", _conn).ExecuteNonQuery();
 			
+			
 						
 		}
 		
@@ -131,12 +132,12 @@ namespace NpgsqlTests
 		  
 		  da.Fill(ds);
 		  
-		  Assertion.AssertEquals(1, ds.Tables.Count);
-		  Assertion.AssertEquals(4, ds.Tables[0].Columns.Count);
-		  Assertion.AssertEquals("field_serial", ds.Tables[0].Columns[0].ColumnName);
-		  Assertion.AssertEquals("field_int2", ds.Tables[0].Columns[1].ColumnName);
-		  Assertion.AssertEquals("field_timestamp", ds.Tables[0].Columns[2].ColumnName);
-		  Assertion.AssertEquals("field_numeric", ds.Tables[0].Columns[3].ColumnName);
+		  Assert.AreEqual(1, ds.Tables.Count);
+		  Assert.AreEqual(4, ds.Tables[0].Columns.Count);
+		  Assert.AreEqual("field_serial", ds.Tables[0].Columns[0].ColumnName);
+		  Assert.AreEqual("field_int2", ds.Tables[0].Columns[1].ColumnName);
+		  Assert.AreEqual("field_timestamp", ds.Tables[0].Columns[2].ColumnName);
+		  Assert.AreEqual("field_numeric", ds.Tables[0].Columns[3].ColumnName);
 		  
 		}
         

+ 49 - 16
mcs/class/Npgsql/Test/DataReaderTests.cs

@@ -66,7 +66,7 @@ namespace NpgsqlTests
 			
 			dr.Read();
 			Boolean result = dr.GetBoolean(4);
-			Assertion.AssertEquals(true, result);
+			Assert.AreEqual(true, result);
 			
 		}
 		
@@ -85,7 +85,7 @@ namespace NpgsqlTests
 			
 			Int64 a = dr.GetChars(1, 0, result, 0, 6);
 			
-			Assertion.AssertEquals("Random", new String(result));
+			Assert.AreEqual("Random", new String(result));
 			/*ConsoleWriter cw = new ConsoleWriter(Console.Out);
 			
 			cw.WriteLine(result);*/
@@ -109,7 +109,7 @@ namespace NpgsqlTests
 			//ConsoleWriter cw = new ConsoleWriter(Console.Out);
 			
 			//cw.WriteLine(result.GetType().Name);
-			Assertion.AssertEquals(4, result);
+			Assert.AreEqual(4, result);
 			
 		}
 		
@@ -126,7 +126,7 @@ namespace NpgsqlTests
 			
 			Int16 result = dr.GetInt16(1);
 			
-			Assertion.AssertEquals(2, result);
+			Assert.AreEqual(2, result);
 			
 		}
 		
@@ -144,7 +144,7 @@ namespace NpgsqlTests
 			Decimal result = dr.GetDecimal(3);
 			
 						
-			Assertion.AssertEquals(4.23M, result);
+			Assert.AreEqual(4.2300000M, result);
 			
 		}
 	
@@ -164,7 +164,7 @@ namespace NpgsqlTests
 			//Double result = Double.Parse(dr.GetInt32(2).ToString());
 		  Double result = dr.GetDouble(2);
 			
-			Assertion.AssertEquals(.123456789012345D, result);
+			Assert.AreEqual(.123456789012345D, result);
 			
 		}
 		
@@ -182,7 +182,7 @@ namespace NpgsqlTests
 			//Single result = Single.Parse(dr.GetInt32(2).ToString());
 		  Single result = dr.GetFloat(1);
 			
-			Assertion.AssertEquals(.123456F, result);
+			Assert.AreEqual(.123456F, result);
 			
 		}
 		
@@ -199,7 +199,7 @@ namespace NpgsqlTests
 			
 			String result = dr.GetString(1);
 			
-			Assertion.AssertEquals("Random text", result);
+			Assert.AreEqual("Random text", result);
 			
 		}
 		
@@ -225,7 +225,7 @@ namespace NpgsqlTests
 			
 			String result = dr.GetString(1);
 			
-			Assertion.AssertEquals(test, result);
+			Assert.AreEqual(test, result);
 			
 		}
 		
@@ -250,7 +250,7 @@ namespace NpgsqlTests
 			
 			String result = dr.GetString(1);
 			
-			Assertion.AssertEquals(test, result);
+			Assert.AreEqual(test, result);
 			
 		}
 		
@@ -267,7 +267,7 @@ namespace NpgsqlTests
 			
 			String result = (String) dr["field_text"];
 			
-			Assertion.AssertEquals("Random text", result);
+			Assert.AreEqual("Random text", result);
 			
 		}
 		
@@ -477,9 +477,9 @@ namespace NpgsqlTests
 			NpgsqlDataReader dr = command.ExecuteReader();
             
             dr.Read();
-            Assertion.AssertEquals(false, dr.IsDBNull(0));
+            Assert.AreEqual(false, dr.IsDBNull(0));
             dr.Read();
-            Assertion.AssertEquals(true, dr.IsDBNull(0));
+            Assert.AreEqual(true, dr.IsDBNull(0));
             
                 
         }
@@ -493,11 +493,44 @@ namespace NpgsqlTests
 			NpgsqlDataReader dr = command.ExecuteReader();
             
             dr.Read();
-            Assertion.AssertEquals(false, dr.IsDBNull(0));
+            Assert.AreEqual(false, dr.IsDBNull(0));
+            
+        }
+        
+        
+        
+        [Test]
+        public void TypesNames()
+        {
+        	_conn.Open();
+            NpgsqlCommand command = new NpgsqlCommand("select * from tablea where 1 = 2;", _conn);
+			
+			NpgsqlDataReader dr = command.ExecuteReader();
+            
+            dr.Read();
+            
+            Assert.AreEqual("int4", dr.GetDataTypeName(0));
+            Assert.AreEqual("text", dr.GetDataTypeName(1));
+            Assert.AreEqual("int4", dr.GetDataTypeName(2));
+            Assert.AreEqual("int8", dr.GetDataTypeName(3));
+            Assert.AreEqual("bool", dr.GetDataTypeName(4));
+            
+            dr.Close();
+            
+            command.CommandText = "select * from tableb where 1 = 2";
+            
+            dr = command.ExecuteReader();
+            
+            dr.Read();
+            
+            Assert.AreEqual("int4", dr.GetDataTypeName(0));
+            Assert.AreEqual("int2", dr.GetDataTypeName(1));
+            Assert.AreEqual("timestamp", dr.GetDataTypeName(2));
+            Assert.AreEqual("numeric", dr.GetDataTypeName(3));
+            
+            
             
         }
-		
-