2005-12-09 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
authorFrancisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com>
Fri, 9 Dec 2005 13:13:36 +0000 (13:13 -0000)
committerFrancisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com>
Fri, 9 Dec 2005 13:13:36 +0000 (13:13 -0000)
    Npgsql/NpgsqlError.cs,
    Npgsql/NpgsqlParameter.cs,
    Npgsql/NpgsqlConnectionString.cs,
    Npgsql/NpgsqlConnection.cs,
    Npgsql/NpgsqlSchema.cs,
    Npgsql/NpgsqlClosedState.cs,
    Npgsql/NpgsqlCommand.cs,
    Npgsql/NpgsqlState.cs,
    Npgsql/NpgsqlAsciiRow.cs,
    Npgsql/NpgsqlConnectorPool.cs,
    Npgsql/NpgsqlConnector.cs,
    Npgsql/NpgsqlQuery.cs,
    Npgsql/NpgsqlRowDescription.cs,
    Npgsql/NpgsqlCommandBuilder.cs,
    Npgsql/NpgsqlDataReader.cs,
    Npgsql/NpgsqlException.cs,
    Npgsql/NpgsqlMediator.cs,
    Npgsql/NpgsqlReadyState.cs,
    Npgsql/NpgsqlEventLog.cs,
    Npgsql.dll.resources,
    NpgsqlTypes/NpgsqlTypeConverters.cs,
    NpgsqlTypes/NpgsqlTypesHelper.cs,
    Makefile: Updated to 1.0beta1

svn path=/trunk/mcs/; revision=54161

24 files changed:
mcs/class/Npgsql/ChangeLog
mcs/class/Npgsql/Makefile
mcs/class/Npgsql/Npgsql.dll.resources
mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs
mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs
mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs
mcs/class/Npgsql/Npgsql/NpgsqlCommandBuilder.cs
mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs
mcs/class/Npgsql/Npgsql/NpgsqlConnectionString.cs
mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs
mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs
mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs
mcs/class/Npgsql/Npgsql/NpgsqlError.cs
mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs
mcs/class/Npgsql/Npgsql/NpgsqlException.cs
mcs/class/Npgsql/Npgsql/NpgsqlMediator.cs
mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs
mcs/class/Npgsql/Npgsql/NpgsqlQuery.cs
mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs
mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs
mcs/class/Npgsql/Npgsql/NpgsqlSchema.cs
mcs/class/Npgsql/Npgsql/NpgsqlState.cs
mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs
mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs

index b3251478d632563bee3cd6eb7cf2e1360f84a57b..5259d5bfc9965a99fe2c1b0b57b48a8dc579df36 100644 (file)
@@ -1,4 +1,33 @@
 
+
+
+2005-12-09  Francisco Figueiredo Jr.  <fxjrlists@yahoo.com.br>
+
+    Npgsql/NpgsqlError.cs,
+    Npgsql/NpgsqlParameter.cs,
+    Npgsql/NpgsqlConnectionString.cs,
+    Npgsql/NpgsqlConnection.cs,
+    Npgsql/NpgsqlSchema.cs,
+    Npgsql/NpgsqlClosedState.cs,
+    Npgsql/NpgsqlCommand.cs,
+    Npgsql/NpgsqlState.cs,
+    Npgsql/NpgsqlAsciiRow.cs,
+    Npgsql/NpgsqlConnectorPool.cs,
+    Npgsql/NpgsqlConnector.cs,
+    Npgsql/NpgsqlQuery.cs,
+    Npgsql/NpgsqlRowDescription.cs,
+    Npgsql/NpgsqlCommandBuilder.cs,
+    Npgsql/NpgsqlDataReader.cs,
+    Npgsql/NpgsqlException.cs,
+    Npgsql/NpgsqlMediator.cs,
+    Npgsql/NpgsqlReadyState.cs,
+    Npgsql/NpgsqlEventLog.cs,
+    Npgsql.dll.resources,
+    NpgsqlTypes/NpgsqlTypeConverters.cs,
+    NpgsqlTypes/NpgsqlTypesHelper.cs,
+    Makefile: Updated to 1.0beta1
+
+
 2005-08-27  Francisco Figueiredo Jr.  <fxjrlists@yahoo.com.br>
     * NpgsqlParameter.cs: Applied patch to fix a NullReferenceException when using Design Time support. Thanks Josh Cooley ( jbnpgsql at tuxinthebox dot net ) for patch.
     * NpgsqlDataReader.cs: Updated ProviderType metadata from NpgsqlDataReader.GetResultsetSchema to be the string for the type rather than the oid.  Fixed ColumnSize, NumericPrecision, NumericScale, BaseColumnName, AllowDBNull, and IsAliased. Also integrated patch from (rlp at bamafolks dot com), gborg 751. Thanks Josh Cooley (jbnpgsql at tuxinthebox dot net) for patches!
index 34a9158755b15bbaf49dab9febd3e9b1f4a91419..9a4d68dd120fb09974f5ffb25f6d2525d0646aea 100755 (executable)
@@ -52,6 +52,7 @@ RESX_RES = \
        Npgsql/NpgsqlEventLog.resources                         \
        Npgsql/NpgsqlException.resources                        \
        Npgsql/PGUtil.resources                                 \
+       Npgsql/NpgsqlConnectionString.resources                                 \
        NpgsqlTypes/NpgsqlTypesHelper.resources                 
 
 include ../../build/library.make
index 0b1671a66819739cb793307562ccbc8c7ec0c3d9..95a224c5da7b782ae65a4c39c1e883ade878926f 100644 (file)
@@ -10,6 +10,7 @@
 -resource:Npgsql/NpgsqlEventLog.resources,Npgsql.NpgsqlEventLog.resources
 -resource:Npgsql/NpgsqlException.resources,Npgsql.NpgsqlException.resources
 -resource:Npgsql/PGUtil.resources,Npgsql.PGUtil.resources
+-resource:Npgsql/NpgsqlConnectionString.resources,Npgsql.NpgsqlConnectionString.resources
 -resource:NpgsqlTypes/NpgsqlTypesHelper.resources,NpgsqlTypes.NpgsqlTypesHelper.resources
 
 
index 5ede3256fc0772a89244704686d11f814cc03c39..d5ccfc8bb4cabd7d14d0d72d3f376895e9ccbfa4 100644 (file)
@@ -44,11 +44,15 @@ namespace Npgsql
         private static readonly String CLASSNAME = "NpgsqlAsciiRow";
 
         private readonly Int16        READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
+        private byte[] _inputBuffer;
+        private char[] _chars;
 
-        public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion)
+        public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion, byte[] inputBuffer, char[] chars)
                 : base(rowDesc, protocolVersion)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
+            _inputBuffer = inputBuffer;
+            _chars = chars;
         }
 
         public override void ReadFromStream(Stream inputStream, Encoding encoding)
@@ -70,7 +74,6 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
 
-            Byte[]       input_buffer = new Byte[READ_BUFFER_SIZE];
             Byte[]       null_map_array = new Byte[(row_desc.NumFields + 7)/8];
 
             Array.Clear(null_map_array, 0, null_map_array.Length);
@@ -79,9 +82,6 @@ namespace Npgsql
             // 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 );
@@ -98,44 +98,15 @@ namespace Npgsql
 
                 // Read the first data of the first row.
 
-                PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, 4);
+                PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, 4);
 
                 NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
-                Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
+                Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(_inputBuffer, 0));
                 field_value_size -= 4;
-                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);
-
-                    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, 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));
-
 
+                               string result = ReadStringFromStream(inputStream, field_value_size, decoder);
                 // Add them to the AsciiRow data.
-                data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result.ToString(), field_descr.type_size, field_descr.type_modifier));
+                data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier));
 
             }
         }
@@ -144,79 +115,36 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
 
-            Byte[] input_buffer = new Byte[READ_BUFFER_SIZE];
-
-            PGUtil.ReadInt32(inputStream, input_buffer);
-            Int16 numCols = PGUtil.ReadInt16(inputStream, input_buffer);
+            PGUtil.ReadInt32(inputStream, _inputBuffer);
+            Int16 numCols = PGUtil.ReadInt16(inputStream, _inputBuffer);
 
             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);
-
-                // Check if this field is null
-                if (field_value_size == -1) // Null value
-                {
-                    data.Add(DBNull.Value);
-                    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)));
-                    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.
-                PGUtil.CheckedStreamRead(inputStream, input_buffer, 0, bytes_left);
-
-                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)));
-
-
-                    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(field_descr.type_info, result.ToString(), field_descr.type_size, field_descr.type_modifier));
-
-                }
-                else
-                    // 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));
+                       for (Int16 field_count = 0; field_count < numCols; field_count++)
+                       {
+                               Int32 field_value_size = PGUtil.ReadInt32(inputStream, _inputBuffer);
+
+                               // Check if this field is null
+                               if (field_value_size == -1) // Null value
+                               {
+                                       data.Add(DBNull.Value);
+                                       continue;
+                               }
+
+                               NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
+
+                               if (row_desc[field_count].format_code == FormatCode.Text)
+                               {
+                                       string result = ReadStringFromStream(inputStream, field_value_size, decoder);
+                                       // Add them to the AsciiRow data.
+                                       data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier));
+                               }
+                               else
+                               {
+                                       PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, Math.Min(field_value_size, _inputBuffer.Length));
+                                       // FIXME: _inputBuffer isn't holding all the field value. This code isn't handling binary data correctly.
+                                       data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, _inputBuffer, encoding, field_value_size, field_descr.type_modifier));
+                               }
             }
         }
 
@@ -233,5 +161,43 @@ namespace Npgsql
             // MSB and test it with the byte 10000000.
             return (((test_byte << (index%8)) & 0x80) == 0);
         }
+
+               private int GetCharsFromStream(Stream inputStream, int count, Decoder decoder, char[] chars)
+               {
+                       // Now, read just the field value.
+                       PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, count);
+                       int charCount = decoder.GetCharCount(_inputBuffer, 0, count);
+                       decoder.GetChars(_inputBuffer, 0, count, chars, 0);
+                       return charCount;
+               }
+
+               private string ReadStringFromStream(Stream inputStream, int field_value_size, Decoder decoder)
+               {
+                       int bytes_left = field_value_size;
+                       int charCount;
+
+                       if (field_value_size > _inputBuffer.Length)
+                       {
+                               StringBuilder   result = new StringBuilder();
+
+                               while (bytes_left > READ_BUFFER_SIZE)
+                               {
+                                       charCount = GetCharsFromStream(inputStream, READ_BUFFER_SIZE, decoder, _chars);
+                                       result.Append(_chars, 0,charCount);
+                                       bytes_left -= READ_BUFFER_SIZE;
+                               }
+
+                               charCount = GetCharsFromStream(inputStream, bytes_left, decoder, _chars);
+                               result.Append(_chars, 0,charCount);
+
+                               return result.ToString();
+                       }
+                       else
+                       {
+                               charCount = GetCharsFromStream(inputStream, bytes_left, decoder, _chars);
+
+                               return new String(_chars, 0,charCount);
+                       }
+               }
     }
 }
index 57d0519b2c3ca65aa9d7970495026e9ce10a8a3e..2376422fec07e858cbdb77217fcb3271d0c63ebf 100644 (file)
@@ -35,7 +35,7 @@ namespace Npgsql
     internal sealed class NpgsqlClosedState : NpgsqlState
     {
 
-        private static NpgsqlClosedState _instance = null;
+        private static NpgsqlClosedState _instance = new NpgsqlClosedState();
         private static readonly String CLASSNAME = "NpgsqlClosedState";
 
         private NpgsqlClosedState() : base()
@@ -45,10 +45,6 @@ namespace Npgsql
             get
             {
                 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Instance");
-                if (_instance == null)
-                {
-                    _instance = new NpgsqlClosedState();
-                }
                 return _instance;
             }
         }
index 285891d55a15ba58e2738899ea8b7bcf4d0b088e..77a86d9abe810b12c339380022e4b7680f96d986 100644 (file)
@@ -251,7 +251,7 @@ namespace Npgsql
         internal NpgsqlConnector Connector {
             get
             {
-                if (connector == null && this.connection != null)
+                if (this.connection != null)
                     connector = this.connection.Connector;
 
                 return connector;
@@ -813,6 +813,8 @@ namespace Npgsql
 
 
                 result = AddSingleRowBehaviorSupport(result);
+                
+                result = AddSchemaOnlyBehaviorSupport(result);
 
                 return result;
             }
@@ -912,7 +914,11 @@ namespace Npgsql
                 return ProcessRefcursorFunctionReturn(result);
 
 
-            return AddSingleRowBehaviorSupport(result);
+            result = AddSingleRowBehaviorSupport(result);
+            
+            result = AddSchemaOnlyBehaviorSupport(result);
+            
+            return result;
         }
         
         
@@ -1226,7 +1232,7 @@ namespace Npgsql
             
             ResultCommandText = ResultCommandText.Trim();
         
-            if ((commandBehavior & CommandBehavior.SingleRow) > 0)
+            if ((commandBehavior & CommandBehavior.SingleRow) == CommandBehavior.SingleRow)
             {
                 if (ResultCommandText.EndsWith(";"))
                     ResultCommandText = ResultCommandText.Substring(0, ResultCommandText.Length - 1);
@@ -1236,6 +1242,24 @@ namespace Npgsql
             
             
             
+            return ResultCommandText;
+            
+        }
+        
+        private String AddSchemaOnlyBehaviorSupport(String ResultCommandText)
+        {
+            
+            ResultCommandText = ResultCommandText.Trim();
+        
+            if ((commandBehavior & CommandBehavior.SchemaOnly) == CommandBehavior.SchemaOnly)
+            {
+                if (ResultCommandText.EndsWith(";"))
+                    ResultCommandText = ResultCommandText.Substring(0, ResultCommandText.Length - 1);
+                ResultCommandText += " limit 0;";
+                
+            }
+            
+            
             return ResultCommandText;
             
         }
@@ -1247,7 +1271,7 @@ namespace Npgsql
             CheckConnectionState();
 
             // reset any responses just before getting new ones
-            connector.Mediator.ResetResponses();
+            Connector.Mediator.ResetResponses();
 
 
             if (parse == null)
index cab07a1b4f0ed081be780aee4a8f8f43f1ff9da1..1d1d67441cdf74353e5734d30d78ccb9b222b39e 100644 (file)
@@ -1,9 +1,9 @@
 // NpgsqlCommandBuilder.cs
 //
 // Author:
-//   Pedro Martínez Juliá (yoros@wanadoo.es)
+//   Pedro Martínez Juliá (yoros@wanadoo.es)
 //
-// Copyright (C) 2003 Pedro Martínez Juliá
+// Copyright (C) 2003 Pedro Martínez Juliá
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -29,6 +29,7 @@ using System;
 using System.Data;
 using System.Data.Common;
 using System.ComponentModel;
+using NpgsqlTypes;
 
 namespace Npgsql
 {
@@ -49,6 +50,9 @@ namespace Npgsql
 
         private string table_name = String.Empty;
 
+        private string quotePrefix = "\"";
+        private string quoteSuffix = "\"";
+
         public NpgsqlCommandBuilder ()
         {}
 
@@ -88,18 +92,19 @@ namespace Npgsql
             }
         }
 
-        private void OnRowUpdating(Object sender, NpgsqlRowUpdatingEventArgs value) {
+        private void OnRowUpdating(Object sender, NpgsqlRowUpdatingEventArgs value)
+        {
             switch (value.StatementType)
             {
-                case StatementType.Insert:
-                    value.Command = GetInsertCommand(value.Row);
-                    break;
-                case StatementType.Update:
-                    value.Command = GetUpdateCommand(value.Row);
-                    break;
-                case StatementType.Delete:
-                    value.Command = GetDeleteCommand(value.Row);
-                    break;
+            case StatementType.Insert:
+                value.Command = GetInsertCommand(value.Row);
+                break;
+            case StatementType.Update:
+                value.Command = GetUpdateCommand(value.Row);
+                break;
+            case StatementType.Delete:
+                value.Command = GetDeleteCommand(value.Row);
+                break;  
             }
 
             DataColumnMappingCollection columnMappings = value.TableMapping.ColumnMappings;
@@ -126,26 +131,77 @@ namespace Npgsql
             value.Row.AcceptChanges ();
         }
 
+
         public string QuotePrefix {
             get
             {
-                return "";
+                return quotePrefix;
             }
             set
-            { }
+            {
+                quotePrefix = value;
+            }
         }
 
+
         public string QuoteSuffix {
             get
             {
-                return "";
+                return quoteSuffix;
             }
             set
-            { }
+            {
+                quoteSuffix = value;
+            }
         }
 
+        ///<summary>
+        ///
+        /// This method is reponsible to derive the command parameter list with values obtained from function definition.
+        /// It clears the Parameters collection of command. Also, if there is any parameter type which is not supported by Npgsql, an InvalidOperationException will be thrown.
+        /// Parameters name will be parameter1, parameter2, ...
+        /// For while, only parameter name and NpgsqlDbType are obtained.
+        ///</summary>
+        /// <param name="command">NpgsqlCommand whose function parameters will be obtained.</param>
+
         public static void DeriveParameters (NpgsqlCommand command)
-        {}
+        {
+            String query = "select proargtypes from pg_proc where proname = :procname";
+
+            NpgsqlCommand c = new NpgsqlCommand(query, command.Connection);
+            c.Parameters.Add(new NpgsqlParameter("procname", NpgsqlDbType.Text));
+            c.Parameters[0].Value = command.CommandText;
+
+            String types = (String) c.ExecuteScalar();
+
+            command.Parameters.Clear();
+            Int32 i = 1;
+
+            foreach(String s in types.Split())
+            {
+                if (!c.Connector.OidToNameMapping.ContainsOID(Int32.Parse(s)))
+                {
+                    command.Parameters.Clear();
+                    throw new InvalidOperationException(String.Format("Invalid parameter type: {0}", s));
+                }
+                command.Parameters.Add(new NpgsqlParameter("parameter" + i++, c.Connector.OidToNameMapping[Int32.Parse(s)].NpgsqlDbType));
+            }
+
+        }
+
+        private string GetQuotedName(string str)
+        {
+            string result = str;
+            if ((QuotePrefix != string.Empty) && !str.StartsWith(QuotePrefix))
+            {
+                result = QuotePrefix + result;
+            }
+            if ((QuoteSuffix != string.Empty) && !str.EndsWith(QuoteSuffix))
+            {
+                result = result + QuoteSuffix;
+            }
+            return result;
+        }
 
         public NpgsqlCommand GetInsertCommand (DataRow row)
         {
@@ -161,17 +217,17 @@ namespace Npgsql
                         fields += ", ";
                         values += ", ";
                     }
-                    fields += column.ColumnName;
+                    fields += GetQuotedName(column.ColumnName);
                     values += ":param_" + column.ColumnName;
                 }
                 if (table_name == String.Empty)
                 {
                     table_name = row.Table.TableName;
                 }
-                NpgsqlCommand cmdaux = new NpgsqlCommand("insert into " + table_name + " (" + fields + ") values (" + values + ")", data_adapter.SelectCommand.Connection);
+                NpgsqlCommand cmdaux = new NpgsqlCommand("insert into " + GetQuotedName(table_name) + " (" + fields + ") values (" + values + ")", data_adapter.SelectCommand.Connection);
                 foreach (DataColumn column in row.Table.Columns)
                 {
-                    NpgsqlParameter aux = new NpgsqlParameter("param_" + column.ColumnName, row[column]);
+                    NpgsqlParameter aux = new NpgsqlParameter("param_" + column.ColumnName, row[column], NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType));
                     aux.Direction = ParameterDirection.Input;
                     aux.SourceColumn = column.ColumnName;
                     cmdaux.Parameters.Add(aux);
@@ -195,17 +251,17 @@ namespace Npgsql
                         wheres += " and ";
                     }
                     DataColumn column = row.Table.Columns[i];
-                    sets += String.Format("{0} = :s_param_{0}", column.ColumnName);
-                    wheres += String.Format("(({0} is null) or ({0} = :w_param_{0}))", column.ColumnName);
+                    sets += String.Format(GetQuotedName("{0}") + " = :s_param_{0}", column.ColumnName);
+                    wheres += String.Format("((" + GetQuotedName("{0}") + " is null) or (" + GetQuotedName("{0}") + " = :w_param_{0}))", column.ColumnName);
                 }
                 if (table_name == String.Empty)
                 {
                     table_name = row.Table.TableName;
                 }
-                NpgsqlCommand cmdaux = new NpgsqlCommand("update " + table_name + " set " + sets + " where ( " + wheres + " )", data_adapter.SelectCommand.Connection);
+                NpgsqlCommand cmdaux = new NpgsqlCommand("update " + GetQuotedName(table_name) + " set " + sets + " where ( " + wheres + " )", data_adapter.SelectCommand.Connection);
                 foreach (DataColumn column in row.Table.Columns)
                 {
-                    NpgsqlParameter aux = new NpgsqlParameter("s_param_" + column.ColumnName, row[column]);
+                    NpgsqlParameter aux = new NpgsqlParameter("s_param_" + column.ColumnName, row[column], NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType));
                     aux.Direction = ParameterDirection.Input;
                     aux.SourceColumn = column.ColumnName;
                     aux.SourceVersion = DataRowVersion.Current;
@@ -213,7 +269,7 @@ namespace Npgsql
                 }
                 foreach (DataColumn column in row.Table.Columns)
                 {
-                    NpgsqlParameter aux = new NpgsqlParameter("w_param_" + column.ColumnName, row[column]);
+                    NpgsqlParameter aux = new NpgsqlParameter("w_param_" + column.ColumnName, row[column], NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType));
                     aux.Direction = ParameterDirection.Input;
                     aux.SourceColumn = column.ColumnName;
                     aux.SourceVersion = DataRowVersion.Original;
@@ -237,16 +293,16 @@ namespace Npgsql
                     {
                         wheres += " and ";
                     }
-                    wheres += String.Format("(({0} is null) or ({0} = :param_{0}))", column.ColumnName);
+                    wheres += String.Format("((" + GetQuotedName("{0}") + " is null) or (" + GetQuotedName("{0}") + " = :param_{0}))", column.ColumnName);
                 }
                 if (table_name == String.Empty)
                 {
                     table_name = row.Table.TableName;
                 }
-                NpgsqlCommand cmdaux = new NpgsqlCommand("delete from " + table_name + " where ( " + wheres + " )", data_adapter.SelectCommand.Connection);
+                NpgsqlCommand cmdaux = new NpgsqlCommand("delete from " + GetQuotedName(table_name) + " where ( " + wheres + " )", data_adapter.SelectCommand.Connection);
                 foreach (DataColumn column in row.Table.Columns)
                 {
-                    NpgsqlParameter aux = new NpgsqlParameter("param_" + column.ColumnName, row[column,DataRowVersion.Original]);
+                    NpgsqlParameter aux = new NpgsqlParameter("param_" + column.ColumnName, row[column,DataRowVersion.Original], NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType));
                     aux.Direction = ParameterDirection.Input;
                     aux.SourceColumn = column.ColumnName;
                     aux.SourceVersion = DataRowVersion.Original;
index df737229a84973b0e4e3a034f30f430d3e8a5bf9..15d74f5424045bedda2c7d68aad18e6f68d263ec 100644 (file)
@@ -140,18 +140,19 @@ namespace Npgsql
         /// <summary>
         /// Gets or sets the string used to connect to a PostgreSQL database.
         /// Valid values are:
-        /// Server:        Address/Name of Postgresql Server;
-        /// Port:          Port to connect to;
-        /// Protocol:      Protocol version to use, instead of automatic; Integer 2 or 3;
-        /// Database:      Database name. Defaults to user name if not specified;
-        /// User Id:       User name;
-        /// Password:      Password for clear text authentication;
-        /// 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;
-        /// MaxPoolSize:   Max size of connection pool;
-        /// Encoding:      Encoding to be used;
-        /// Timeout:       Time to wait for connection open in seconds.
+        /// Server:             Address/Name of Postgresql Server;
+        /// Port:               Port to connect to;
+        /// Protocol:           Protocol version to use, instead of automatic; Integer 2 or 3;
+        /// Database:           Database name. Defaults to user name if not specified;
+        /// User Id:            User name;
+        /// Password:           Password for clear text authentication;
+        /// 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;
+        /// MaxPoolSize:        Max size of connection pool;
+        /// Encoding:           Encoding to be used;
+        /// Timeout:            Time to wait for connection open in seconds.
+        /// ConnectionLifeTime: Time to wait before closing unused connections in the pool.
         /// </summary>
         /// <value>The connection string that includes the server name,
         /// the database name, and other parameters needed to establish
@@ -229,6 +230,24 @@ namespace Npgsql
                 return connection_string.ToInt32(ConnectionStringKeys.Timeout, ConnectionStringDefaults.Timeout);
             }
         }
+        
+        /// <summary>
+        /// Gets the time to wait before closing unused connections in the pool if the count
+        /// of all connections exeeds MinPoolSize.
+        /// </summary>
+        /// <remarks>
+        /// If connection pool contains unused connections for ConnectionLifeTime seconds,
+        /// the half of them will be closed. If there will be unused connections in a second
+        /// later then again the half of them will be closed and so on.
+        /// This strategy provide smooth change of connection count in the pool.
+        /// </remarks>
+        /// <value>The time (in seconds) to wait. The default value is 15 seconds.</value>
+        public Int32 ConnectionLifeTime {
+            get
+            {
+                return connection_string.ToInt32(ConnectionStringKeys.ConnectionLifeTime, ConnectionStringDefaults.ConnectionLifeTime);
+            }
+        }
 
         ///<summary>
         /// Gets the name of the current database or the database to be used after a connection is opened.
index 820726ac4fa4d1d660a088ad3bfc1fc92b2a9d89..c054add865fdeae92e4c3e78dd928d78c17eb5dc 100644 (file)
@@ -376,21 +376,22 @@ namespace Npgsql
     /// </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 SslMode          = "SSLMODE";
-        public static readonly String Encoding         = "ENCODING";
-        public static readonly String Timeout          = "TIMEOUT";
-
+        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 SslMode               = "SSLMODE";
+        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";
+        public static readonly String Pooling               = "POOLING";
+        public static readonly String ConnectionLifeTime    = "CONNECTIONLIFETIME";
+        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
@@ -431,12 +432,13 @@ namespace Npgsql
     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
+        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
+        public static readonly Int32 ConnectionLifeTime     = 15; // Seconds
     }
     
     internal enum SslMode
index f4b730f7ffa34a859728bea30835c0bc51dbab53..732da5507feafbcfeb38793520df1f2953402139 100644 (file)
@@ -252,7 +252,8 @@ namespace Npgsql
             try
             {
                 // Here we use a fake NpgsqlCommand, just to send the test query string.
-                Query(new NpgsqlCommand("select 1 as ConnectionTest"));
+                
+                Query(new NpgsqlCommand("select 1 as ConnectionTest", this));
                 
                 // Clear mediator.
                 Mediator.ResetResponses();
@@ -279,7 +280,7 @@ namespace Npgsql
             if (_planIndex > 0)
             {
                 for(i = 1; i <= _planIndex; i++)
-                    Query(new NpgsqlCommand(String.Format("deallocate \"{0}\";", _planNamePrefix + i)));
+                    Query(new NpgsqlCommand(String.Format("deallocate \"{0}\";", _planNamePrefix + i), this));
             }
 
             _portalIndex = 0;
index 91567f215bf4d1aa9162d75d1aec430ffa04dd44..dac1c40aed1600b45373d813c64ab54e8e68b2c2 100644 (file)
@@ -26,6 +26,7 @@
 using System;
 using System.Collections;
 using System.Threading;
+using System.Timers;
 
 namespace Npgsql
 {
@@ -44,6 +45,10 @@ namespace Npgsql
             /// are currently in use.
             /// </summary>
             public Int32            UseCount = 0;
+            public Int32            ConnectionLifeTime;
+            public Int32            InactiveTime = 0;
+            public Int32            MinPoolSize;
+
         }
 
         /// <value>Unique static instance of the connector pool
@@ -53,9 +58,63 @@ namespace Npgsql
         public NpgsqlConnectorPool()
         {
             PooledConnectors = new Hashtable();
+            Timer = new System.Timers.Timer(1000);
+            Timer.AutoReset = true;
+            Timer.Elapsed += new ElapsedEventHandler(TimerElapsedHandler);
+            Timer.Start();
+
+        }
+        
+        
+        ~NpgsqlConnectorPool()
+        {
+            Timer.Stop();
+        }
+        
+        private void TimerElapsedHandler(object sender, ElapsedEventArgs e)
+        {
+            NpgsqlConnector     Connector;
+            lock (this)
+            {
+                foreach (ConnectorQueue Queue in PooledConnectors.Values)
+                {
+                    if (Queue.Count > 0)
+                    {
+                        if (Queue.Count + Queue.UseCount > Queue.MinPoolSize)
+                        {
+                            if (Queue.InactiveTime >= Queue.ConnectionLifeTime)
+                            {
+                                Int32 diff = Queue.Count + Queue.UseCount - Queue.MinPoolSize;
+                                Int32 toBeClosed = (diff + 1) / 2;
+                                if (diff < 2)
+                                    diff = 2;
+                                Queue.InactiveTime -= Queue.ConnectionLifeTime / (int)(Math.Log(diff) / Math.Log(2));
+                                for (Int32 i = 0; i < toBeClosed; ++i)
+                                {
+                                    Connector = (NpgsqlConnector)Queue.Dequeue();
+                                    Connector.Close();
+                                }
+                            }
+                            else
+                            {
+                                Queue.InactiveTime++;
+                            }
+                        }
+                        else
+                        {
+                            Queue.InactiveTime = 0;
+                        }
+                    }
+                    else
+                    {
+                        Queue.InactiveTime = 0;
+                    }
+                }
+            }
         }
 
 
+
         /// <value>Map of index to unused pooled connectors, avaliable to the
         /// next RequestConnector() call.</value>
         /// <remarks>This hashmap will be indexed by connection string.
@@ -69,6 +128,12 @@ namespace Npgsql
         // To be implemented
         //private Hashtable SharedConnectors;
 
+                    
+        /// <value>Timer for tracking unused connections in pools.</value>
+        // I used System.Timers.Timer because of bad experience with System.Threading.Timer
+        // on Windows - it's going mad sometimes and don't respect interval was set.
+        private System.Timers.Timer Timer;
+
         /// <summary>
         /// Searches the shared and pooled connector lists for a
         /// matching connector object or creates a new one.
@@ -240,6 +305,8 @@ namespace Npgsql
             if (Queue == null)
             {
                 Queue = new ConnectorQueue();
+                Queue.ConnectionLifeTime = Connection.ConnectionLifeTime;
+                Queue.MinPoolSize = Connection.MinPoolSize;
                 PooledConnectors[Connection.ConnectionString.ToString()] = Queue;
             }
 
@@ -258,7 +325,8 @@ namespace Npgsql
                         break;
                     }
 
-                    Queue.UseCount--;
+                    // Don't need - we dequeue connector = decrease Queue.Count.
+                    //Queue.UseCount--;
 
                     if (Queue.Count <= 0)
                         return GetPooledConnector(Connection);
index 70fc86dc07c4054dd463887bcec70f26e1ecccc9..a9fdd6eda108ca97ea984f5b9e9c513eadf0488d 100644 (file)
@@ -248,6 +248,7 @@ namespace Npgsql
         /// </summary>
         public DataTable GetSchemaTable()
         {
+            
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable");
 
             if(_currentResultsetSchema == null)
@@ -291,6 +292,7 @@ namespace Npgsql
         /// <summary>
         /// Return the data type OID of the column at index <param name="Index"></param>.
         /// </summary>
+        /// FIXME: Why this method returns String?
         public String GetDataTypeOID(Int32 Index)
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
@@ -698,7 +700,13 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "FillSchemaTable_v2");
             NpgsqlRowDescription rd = _currentResultset.RowDescription;
-            ArrayList keyList = GetPrimaryKeys(GetTableNameFromQuery());
+            ArrayList keyList = null;
+                       
+                       if ((_behavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo)
+                       {
+                               keyList = GetPrimaryKeys(GetTableNameFromQuery());
+                       }
+
             DataRow row;
 
             for (Int16 i = 0; i < rd.NumFields; i++)
@@ -750,31 +758,39 @@ namespace Npgsql
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "FillSchemaTable_v3");
             NpgsqlRowDescription rd = _currentResultset.RowDescription;
 
-            ArrayList tableOids = new ArrayList();
-            for(short i=0; i<rd.NumFields; ++i)
-            {
-                if (rd[i].table_oid != 0 && !tableOids.Contains(rd[i].table_oid))
-                    tableOids.Add(rd[i].table_oid);
-            }
-            Hashtable oidTableLookup = GetTablesFromOids(tableOids);
-
-            ArrayList keyList = null;
-            if (oidTableLookup != null && oidTableLookup.Count == 1)
-            {
-                // only 1, but we can't index into the Hashtable
-                foreach(DictionaryEntry entry in oidTableLookup)
-                {
-                    GetPrimaryKeys(((object[])entry.Value)[Tables.table_name].ToString());
-                }
-            }
-
-            Hashtable columnLookup = GetColumns();
+                       Hashtable oidTableLookup = null;
+                       KeyLookup keyLookup = new KeyLookup();
+                       Hashtable columnLookup = null;
+
+                       if ((_behavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo)
+                       {
+                               ArrayList tableOids = new ArrayList();
+                               for(short i=0; i<rd.NumFields; ++i)
+                               {
+                                       if (rd[i].table_oid != 0 && !tableOids.Contains(rd[i].table_oid))
+                                               tableOids.Add(rd[i].table_oid);
+                               }
+                               oidTableLookup = GetTablesFromOids(tableOids);
+
+                               if (oidTableLookup != null && oidTableLookup.Count == 1)
+                               {
+                                       // only 1, but we can't index into the Hashtable
+                                       foreach(DictionaryEntry entry in oidTableLookup)
+                                       {
+                                               keyLookup = GetKeys((Int32)entry.Key);
+                                       }
+                               }
+
+                               columnLookup = GetColumns();
+                       }
 
             DataRow row;
             for (Int16 i = 0; i < rd.NumFields; i++)
             {
                 row = schema.NewRow();
 
+                               string baseColumnName = GetBaseColumnName(columnLookup, i);
+
                 row["ColumnName"] = GetName(i);
                 row["ColumnOrdinal"] = i + 1;
                 if (rd[i].type_modifier != -1 && (rd[i].type_info.Name == "varchar" || rd[i].type_info.Name == "bpchar"))
@@ -793,8 +809,8 @@ namespace Npgsql
                     row["NumericPrecision"] = 0;
                     row["NumericScale"] = 0;
                 }
-                row["IsUnique"] = false;
-                row["IsKey"] = IsKey(GetName(i), keyList);
+                row["IsUnique"] = IsUnique(keyLookup, baseColumnName);
+                row["IsKey"] = IsKey(keyLookup, baseColumnName);
                 if (rd[i].table_oid != 0 && oidTableLookup != null)
                 {
                     row["BaseCatalogName"] = ((object[])oidTableLookup[rd[i].table_oid])[Tables.table_catalog];
@@ -807,14 +823,14 @@ namespace Npgsql
                     row["BaseSchemaName"] = "";
                     row["BaseTableName"] = "";
                 }
-                row["BaseColumnName"] = GetBaseColumnName(columnLookup, i);
+                row["BaseColumnName"] = baseColumnName;
                 row["DataType"] = GetFieldType(i);
                 row["AllowDBNull"] = IsNullable(columnLookup, i);
                 row["ProviderType"] = rd[i].type_info.Name;
-                row["IsAliased"] = string.CompareOrdinal((string)row["ColumnName"], (string)row["BaseColumnName"]) != 0;
+                row["IsAliased"] = string.CompareOrdinal((string)row["ColumnName"], baseColumnName) != 0;
                 row["IsExpression"] = false;
                 row["IsIdentity"] = false;
-                row["IsAutoIncrement"] = false;
+                row["IsAutoIncrement"] = IsAutoIncrement(columnLookup, i);
                 row["IsRowVersion"] = false;
                 row["IsHidden"] = false;
                 row["IsLong"] = false;
@@ -868,7 +884,104 @@ namespace Npgsql
             return result;
         }
 
-
+               private bool IsKey(KeyLookup keyLookup, string fieldName)
+               {
+                       if (keyLookup.primaryKey == null || keyLookup.primaryKey.Count == 0)
+                               return false;
+
+                       for (int i=0; i<keyLookup.primaryKey.Count; ++i)
+                       {
+                if (fieldName == (String)keyLookup.primaryKey[i])
+                                       return true;
+                       }
+
+                       return false;
+               }
+
+               private bool IsUnique(KeyLookup keyLookup, string fieldName)
+               {
+                       if (keyLookup.uniqueColumns == null || keyLookup.uniqueColumns.Count == 0)
+                               return false;
+
+                       for (int i=0; i<keyLookup.uniqueColumns.Count; ++i)
+                       {
+                if (fieldName == (String)keyLookup.uniqueColumns[i])
+                                       return true;
+                       }
+
+                       return false;
+               }
+
+               private struct KeyLookup
+               {
+                       /// <summary>
+                       /// Contains the column names as the keys
+                       /// </summary>
+                       public ArrayList primaryKey;
+                       /// <summary>
+                       /// Contains all unique columns
+                       /// </summary>
+                       public ArrayList uniqueColumns;
+               }
+
+               private KeyLookup GetKeys(Int32 tableOid)
+               {
+      
+                       string getKeys = "select a.attname, ci.relname, i.indisprimary from pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid AND a.attrelid=ci.oid AND i.indisunique AND ct.oid = :tableOid order by ci.relname";
+
+                       KeyLookup lookup = new KeyLookup();
+                       lookup.primaryKey = new ArrayList();
+                       lookup.uniqueColumns = new ArrayList();
+
+                       using (NpgsqlConnection metadataConn = _connection.Clone())
+                       {
+                               NpgsqlCommand c = new NpgsqlCommand(getKeys, metadataConn);
+                               c.Parameters.Add(new NpgsqlParameter("tableOid", NpgsqlDbType.Integer)).Value = tableOid;
+
+                               using (NpgsqlDataReader dr = c.ExecuteReader())
+                               {
+                                       string previousKeyName = null;
+                                       string possiblyUniqueColumn = null;
+                                       string columnName;
+                                       string currentKeyName;
+                                       // loop through adding any column that is primary to the primary key list
+                                       // add any column that is the only column for that key to the unique list
+                                       // unique here doesn't mean general unique constraint (with possibly multiple columns)
+                                       // it means all values in this single column must be unique
+                                       while (dr.Read())
+                                       {
+         
+                                               columnName = dr.GetString(0);
+                                               currentKeyName = dr.GetString(1);
+                                               // if i.indisprimary
+                                               if (dr.GetBoolean(2))
+                                               {
+                                                       // add column name as part of the primary key
+                                                       lookup.primaryKey.Add(columnName);
+                                               }
+                                               if (currentKeyName != previousKeyName)
+                                               {
+                                                       if (possiblyUniqueColumn != null)
+                                                       {
+                                                               lookup.uniqueColumns.Add(possiblyUniqueColumn);
+                                                       }
+                                                       possiblyUniqueColumn = columnName;
+                                               }
+                                               else
+                                               {
+                                                       possiblyUniqueColumn = null;
+                                               }
+                                               previousKeyName = currentKeyName;
+                                       }
+                                       // if finished reading and have a possiblyUniqueColumn name that is
+                                       // not null, then it is the name of a unique column
+                                       if (possiblyUniqueColumn != null)
+                                               lookup.uniqueColumns.Add(possiblyUniqueColumn);
+                               }
+                       }
+
+                       return lookup;
+               }
 
         private Boolean IsNullable(Hashtable columnLookup, Int32 FieldIndex)
         {
@@ -896,6 +1009,19 @@ namespace Npgsql
                 return GetName(FieldIndex);
         }
 
+               private bool IsAutoIncrement(Hashtable columnLookup, Int32 FieldIndex)
+               {
+                       if (columnLookup == null || _currentResultset.RowDescription[FieldIndex].table_oid == 0)
+                               return false;
+
+                       string lookupKey = _currentResultset.RowDescription[FieldIndex].table_oid.ToString() + "," + _currentResultset.RowDescription[FieldIndex].column_attribute_number;
+                       object[] row = (object[])columnLookup[lookupKey];
+                       if (row != null)
+                               return row[Columns.column_default].ToString().StartsWith("nextval(");
+                       else
+                               return true;
+               }
+
 
         ///<summary>
         /// This methods parses the command text and tries to get the tablename
@@ -973,6 +1099,7 @@ namespace Npgsql
             public const int column_notnull = 1;
             public const int table_id = 2;
             public const int column_num = 3;
+                       public const int column_default = 4;
         }
 
         private Hashtable GetColumns()
@@ -981,8 +1108,8 @@ namespace Npgsql
 
             // the column index is used to find data.
             // any changes to the order of the columns needs to be reflected in struct Columns
-            sb.Append("SELECT a.attname AS column_name, a.attnotnull AS column_notnull, a.attrelid AS table_id, a.attnum AS column_num");
-            sb.Append(" from pg_attribute a WHERE a.attnum > 0 AND (");
+            sb.Append("SELECT a.attname AS column_name, a.attnotnull AS column_notnull, a.attrelid AS table_id, a.attnum AS column_num, d.adsrc as column_default");
+            sb.Append(" FROM pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attnum > 0 AND (");
             bool first = true;
             for(int i=0; i<_currentResultset.RowDescription.NumFields; ++i)
             {
index ac9eefef7681a897c08408449277a4aba56b4574..c428b2b52cd5e1e454cf0e87e82135b42dde3626 100644 (file)
@@ -55,16 +55,17 @@ namespace Npgsql
         private static readonly String CLASSNAME = "NpgsqlError";
 
         private ProtocolVersion protocol_version;
-        private String _severity = "";
-        private String _code = "";
-        private String _message = "";
-        private String _detail = "";
-        private String _hint = "";
-        private String _position = "";
-        private String _where = "";
-        private String _file = "";
-        private String _line = "";
-        private String _routine = "";
+        private String _severity = String.Empty;
+        private String _code = String.Empty;
+        private String _message = String.Empty;
+        private String _detail = String.Empty;
+        private String _hint = String.Empty;
+        private String _position = String.Empty;
+        private String _where = String.Empty;
+        private String _file = String.Empty;
+        private String _line = String.Empty;
+        private String _routine = String.Empty;
+        private String _errorSql = String.Empty;
 
         /// <summary>
         /// Severity code.  All versions.
@@ -175,7 +176,21 @@ namespace Npgsql
                 return _routine;
             }
         }
-
+        
+        /// <summary>
+        /// String containing the sql sent which produced this error.
+        /// </summary>
+        public String ErrorSql
+        {
+            set
+            {
+                _errorSql = value;
+            }
+            get
+            {
+                return _errorSql;
+            }
+        }
         /// <summary>
         /// Return a string representation of this error object.
         /// </summary>
index b1a28bbe670114b8747d4368accd3119481bb4ee..e50d070cae60dac1ba8944ab6cfd271b65ee2ca7 100644 (file)
@@ -151,13 +151,16 @@ namespace Npgsql
             {
                 if (logfile != "")
                 {
+                    lock(logfile)
+                    {
 
-                    StreamWriter writer = new StreamWriter(logfile, true);
+                        StreamWriter writer = new StreamWriter(logfile, true);
 
-                    // The format of the logfile is
-                    // [Date] [Time]  [PID]  [Level]  [Message]
-                    writer.WriteLine(System.DateTime.Now + "\t" + proc.Id + "\t" + msglevel + "\t" + message);
-                    writer.Close();
+                        // The format of the logfile is
+                        // [Date] [Time]  [PID]  [Level]  [Message]
+                        writer.WriteLine(System.DateTime.Now + "\t" + proc.Id + "\t" + msglevel + "\t" + message);
+                        writer.Close();
+                    }
                 }
             }
         }
index 8f5d4950b980e9cb30d9b8fb021d76ed27a62232..7b28fe4e8985539732f1504b2afc3ac4925d460b 100644 (file)
@@ -204,6 +204,18 @@ namespace Npgsql
                 return this[0].Routine;
             }
         }
+        
+        /// <summary>
+        /// String containing the sql sent which produced this error.
+        /// </summary>
+        public String ErrorSql
+        {
+            get
+            {
+                return this[0].ErrorSql;
+            }
+        }
+                
 
         /// <summary>
         /// Returns the entire list of errors provided by the PostgreSQL backend.
index ea7ff00ad15d72e4087b9507f2030944cde9fdaf..0e08e15358e0588dfd48059e731eb6757a9cde2b 100644 (file)
@@ -49,15 +49,17 @@ namespace Npgsql
         //
         // Responses collected from the backend.
         //
-        private ArrayList                                                      _errors;
-        private ArrayList                                                      _notices;
-        private        ArrayList                                                       _resultSets;
-        private ArrayList                                                      _responses;
-        private ArrayList             _notifications;
-        private ListDictionary        _parameters;
-        private NpgsqlBackEndKeyData  _backend_key_data;
+        private ArrayList                              _errors;
+        private ArrayList                              _notices;
+        private        ArrayList                               _resultSets;
+        private ArrayList                              _responses;
+        private ArrayList               _notifications;
+        private ListDictionary          _parameters;
+        private NpgsqlBackEndKeyData    _backend_key_data;
         private NpgsqlRowDescription   _rd;
-        private ArrayList                                                      _rows;
+        private ArrayList                              _rows;
+        private String                  _sqlSent;
+        
 
         public NpgsqlMediator()
         {
@@ -70,6 +72,7 @@ namespace Npgsql
             _notifications = new ArrayList();
             _parameters = new ListDictionary(CaseInsensitiveComparer.Default);
             _backend_key_data = null;
+            _sqlSent = String.Empty;
         }
 
         public void ResetExpectations()
@@ -86,6 +89,7 @@ namespace Npgsql
             _notifications.Clear();
             _parameters.Clear();
             _backend_key_data = null;
+            _sqlSent = String.Empty;
         }
 
 
@@ -168,6 +172,19 @@ namespace Npgsql
                 return _backend_key_data;
             }
         }
+        
+        public String SqlSent
+        {
+            set
+            {
+                _sqlSent = value;
+            }
+            
+            get
+            {
+                return _sqlSent;
+            }
+        }
 
         public void AddNotification(NpgsqlNotificationEventArgs data)
         {
index cb0db91880423508ff438957d683af67e01daa6e..819a3fb9eb65de904a6e19c6f6dbd918fe6a6500 100644 (file)
@@ -65,7 +65,8 @@ namespace Npgsql
         private DataRowVersion             source_version = DataRowVersion.Current;
         private Object                             value = DBNull.Value;
         private System.Resources.ResourceManager resman;
-
+        
+        
         /// <summary>
 
         /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> class.
@@ -74,7 +75,7 @@ namespace Npgsql
         {
             resman = new System.Resources.ResourceManager(this.GetType());
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
-            type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String));
+            //type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String));
         }
 
         /// <summary>
@@ -114,10 +115,26 @@ namespace Npgsql
                 {
                     throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value.GetType()));
                 }
+                
+                
 
             }
         }
-
+               
+               /// <summary>
+               /// Internal constructor to handle parameter creation from CommandBuilder passing a NpgsqlNativeTypeInfo directly.
+               /// </summary>
+               internal NpgsqlParameter(String parameterName, object value, NpgsqlNativeTypeInfo type_info)
+               {
+                       resman = new System.Resources.ResourceManager(this.GetType());
+                       NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, parameterName, value, type_info);
+                       
+                       this.ParameterName = parameterName;
+                       this.value = (value == null) ? DBNull.Value : value;
+                       
+                       this.type_info = (type_info == null) ? NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)) : type_info;
+               }
+               
         /// <summary>
         /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>
         /// class with the parameter name and the data type.
@@ -354,6 +371,8 @@ namespace Npgsql
         {
             get
             {
+               if (type_info == null)
+                       type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String));
                 return type_info;
             }
         }
index e73110f33dff42ce47bae19c7b4915608cdc9e1b..a8818ea89b0c7a90f3b67139031b8406d6760d1d 100644 (file)
@@ -49,6 +49,11 @@ namespace Npgsql
 
 
             String commandText = _command.GetCommandText();
+            
+            // Tell to mediator what command is being sent.
+            
+            _command.Connector.Mediator.SqlSent = commandText;
+            
             // Send the query to server.
             // Write the byte 'Q' to identify a query message.
             outputStream.WriteByte((Byte)'Q');
index 319eb1e07b75df1646fceeb5920615d34e8db5a8..faf89c545c98ad6432007ff49d4c5f25f735a8d7 100644 (file)
@@ -34,7 +34,7 @@ namespace Npgsql
 
     internal sealed class NpgsqlReadyState : NpgsqlState
     {
-        private static NpgsqlReadyState _instance = null;
+        private static NpgsqlReadyState _instance = new NpgsqlReadyState();
 
 
         // Flush and Sync messages. It doesn't need to be created every time it is called.
@@ -51,10 +51,6 @@ namespace Npgsql
         {
             get
             {
-                if ( _instance == null )
-                {
-                    _instance = new NpgsqlReadyState();
-                }
                 return _instance;
             }
         }
index fecf3371f527b0c4b31ad9e28c712b7e98faf2fc..fab11b32f4ba3226b0aa8e9b978c195f77aedb92 100644 (file)
@@ -64,8 +64,8 @@ namespace Npgsql
         private static readonly String CLASSNAME = "NpgsqlRowDescription";
 
 
-        private ArrayList                fields_data = new ArrayList();
-        private ArrayList                fields_index = new ArrayList();
+        private NpgsqlRowDescriptionFieldData[]  fields_data;
+        private string[]                fields_index;
 
         private ProtocolVersion          protocol_version;
 
@@ -103,6 +103,8 @@ namespace Npgsql
             // Temporary FieldData object to get data from stream and put in array.
             NpgsqlRowDescriptionFieldData fd;
 
+                       fields_data = new NpgsqlRowDescriptionFieldData[num_fields];
+                       fields_index = new string[num_fields];
             // Now, iterate through each field getting its data.
             for (Int16 i = 0; i < num_fields; i++)
             {
@@ -120,9 +122,9 @@ namespace Npgsql
                 fd.type_modifier = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 6));
 
                 // Add field data to array.
-                fields_data.Add(fd);
+                fields_data[i] = fd;
 
-                fields_index.Add(fd.name);
+                fields_index[i] = fd.name;
             }
         }
 
@@ -140,6 +142,8 @@ namespace Npgsql
             // Temporary FieldData object to get data from stream and put in array.
             NpgsqlRowDescriptionFieldData fd;
 
+                       fields_data = new NpgsqlRowDescriptionFieldData[num_fields];
+                       fields_index = new string[num_fields];
             for (Int16 i = 0; i < num_fields; i++)
             {
                 fd = new NpgsqlRowDescriptionFieldData();
@@ -153,8 +157,8 @@ namespace Npgsql
                 fd.type_modifier = PGUtil.ReadInt32(input_stream, input_buffer);
                 fd.format_code = (FormatCode)PGUtil.ReadInt16(input_stream, input_buffer);
 
-                fields_data.Add(fd);
-                fields_index.Add(fd.name);
+                               fields_data[i] = fd;
+                               fields_index[i] = fd.name;
             }
         }
 
@@ -162,7 +166,7 @@ namespace Npgsql
         {
             get
             {
-                return (NpgsqlRowDescriptionFieldData)fields_data[index];
+                return fields_data[index];
             }
         }
 
@@ -170,36 +174,28 @@ namespace Npgsql
         {
             get
             {
-                return (Int16)fields_data.Count;
+                return (Int16)fields_data.Length;
             }
         }
 
         public Int16 FieldIndex(String fieldName)
         {
-            Int16 result = -1;
-
-            // First try to find the index with IndexOf (case-sensitive)
-            result = (Int16)fields_index.IndexOf(fieldName);
-
-            if (result > -1)
-            {
-                return result;
-            }
-            else
-            {
-
-                result = 0;
-                foreach (String name in fields_index)
-                {
-
-                    if (name.ToLower().Equals(fieldName.ToLower()))
-                    {
-                        return result;
-                    }
-                    result++;
-                }
-
-            }
+                       // First try to find the index with IndexOf (case-sensitive)
+                       Int16 result = (Int16)Array.IndexOf(fields_index, fieldName, 0, fields_index.Length);
+
+                       if (result != -1)
+                       {
+                               return result;
+                       }
+                       else
+                       {
+                               foreach(string name in fields_index)
+                               {
+                                       ++result;
+                                       if (string.Compare(name, fieldName, true) == 0)
+                                               return result;
+                               }
+                       }
             
             throw new ArgumentOutOfRangeException("fieldName", fieldName, "Field name not found");
 
index d3fd36a8bcfa554b3b5b5113f91267d0a5b4501d..ff45da474dcce3ad62e99bfc43336254e7318c37 100644 (file)
@@ -111,7 +111,10 @@ namespace Npgsql
                     if (restrictions[i] != null && restrictions[i].Length != 0)
                     {
                         if (addWhere)
+                        {
                             query.Append(" WHERE ");
+                            addWhere = false;
+                        }
                         query.AppendFormat("{0} = :{0}", names[i]);
 
                         command.Parameters.Add(new NpgsqlParameter(names[i], restrictions[i]));
index 2f6abc49ce74e0b8fc32148fbd1c0063117c1321..c579fa9b85b8cb8ef71787cb92f0c6b4ae86f626 100644 (file)
@@ -45,6 +45,10 @@ namespace Npgsql
         private readonly String CLASSNAME = "NpgsqlState";
         protected static ResourceManager resman = new ResourceManager(typeof(NpgsqlState));
 
+        internal NpgsqlState()
+        {
+        }
+
         public virtual void Open(NpgsqlConnector context)
         {
             throw new InvalidOperationException("Internal Error! " + this);
@@ -158,6 +162,9 @@ namespace Npgsql
 
             Boolean readyForQuery = false;
 
+            byte[] asciiRowBytes = new byte[300];
+            char[] asciiRowChars = new char[300];
+
             while (!readyForQuery)
             {
                 // Check the first Byte of response.
@@ -168,6 +175,7 @@ namespace Npgsql
                     {
                         NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                         error.ReadFromStream(stream, context.Encoding);
+                        error.ErrorSql = mediator.SqlSent;
 
                         mediator.Errors.Add(error);
 
@@ -300,7 +308,7 @@ namespace Npgsql
                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");
 
                     {
-                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion);
+                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                         asciiRow.ReadFromStream(stream, context.Encoding);
 
                         // Add this row to the rows array.
@@ -425,6 +433,9 @@ namespace Npgsql
 
             Boolean readyForQuery = false;
 
+            byte[] asciiRowBytes = new byte[300];
+            char[] asciiRowChars = new char[300];
+
             while (!readyForQuery)
             {
                 // Check the first Byte of response.
@@ -436,6 +447,7 @@ namespace Npgsql
                     {
                         NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                         error.ReadFromStream(stream, context.Encoding);
+                        error.ErrorSql = mediator.SqlSent;
 
                         mediator.Errors.Add(error);
 
@@ -566,7 +578,7 @@ namespace Npgsql
                     // This is the AsciiRow message.
                     NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
                     {
-                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion);
+                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                         asciiRow.ReadFromStream(stream, context.Encoding);
 
                         // Add this row to the rows array.
index 50d53d7d8bebacf8b38eeb723edb253eb29e64c0..c50e260e7fd29cd93c100b7097d1563e114959e4 100644 (file)
@@ -47,39 +47,39 @@ namespace NpgsqlTypes
         };
 
         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"
+               {
+                       "HH:mm:ss.ffffff",
+                       "HH:mm:ss",
+                       "HH:mm:ss.ffffffzz",
+                       "HH:mm:sszz",
+                       "HH:mm:ss.fffff",
+                       "HH:mm:ss.ffff",
+                       "HH:mm:ss.fff",
+                       "HH:mm:ss.ff",
+                       "HH:mm:ss.f",
+                       "HH:mm:ss.fffffzz",
+                       "HH:mm:ss.ffffzz",
+                       "HH:mm:ss.fffzz",
+                       "HH:mm:ss.ffzz",
+                       "HH:mm:ss.fzz",
         };
 
         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"
+               {
+                       "yyyy-MM-dd HH:mm:ss.ffffff",
+                       "yyyy-MM-dd HH:mm:ss",
+                       "yyyy-MM-dd HH:mm:ss.ffffffzz",
+                       "yyyy-MM-dd HH:mm:sszz",
+                       "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.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",
         };
 
         /// <summary>
index fe58fbeb52604be501af861cca879db6070faa21..6f3e1777c8054fc3755040d068f9424b0d259fc8 100644 (file)
@@ -169,7 +169,7 @@ namespace NpgsqlTypes
                 
                 NativeTypeMapping.AddType("refcursor", NpgsqlDbType.Refcursor, DbType.String, true, null);
                 
-               NativeTypeMapping.AddType("char", NpgsqlDbType.Char, DbType.String, true, null);
+                NativeTypeMapping.AddType("char", NpgsqlDbType.Char, DbType.String, true, null);
                 
                 NativeTypeMapping.AddType("varchar", NpgsqlDbType.Varchar, DbType.String, true, null);