remove svn:executable from .cs files
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlCommand.cs
old mode 100755 (executable)
new mode 100644 (file)
index bd8dae4..e888313
 // 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;
+
+#if WITHDESIGN
 using Npgsql.Design;
+#endif
 
 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>
+    #if WITHDESIGN
     [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlCommand)), ToolboxItem(true)]
-    public sealed class NpgsqlCommand : Component, IDbCommand
+    #endif
+    public sealed class NpgsqlCommand : Component, IDbCommand, ICloneable
     {
+        // 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;
         private CommandType                 type;
         private NpgsqlParameterCollection   parameters;
         private String                      planName;
-        private static Int32                planIndex = 0;
-        private static Int32                portalIndex = 0;
 
         private NpgsqlParse                 parse;
         private NpgsqlBind                  bind;
 
-        // Logging related values
-        private static readonly String CLASSNAME = "NpgsqlCommand";
-        private System.Resources.ResourceManager resman;
+        private Boolean                                                invalidTransactionDetected = false;
+        
+        private CommandBehavior             commandBehavior;
 
         // Constructors
 
@@ -89,26 +95,40 @@ 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;
+            commandBehavior = CommandBehavior.Default;
+            
+            
         }
 
-        /*
         /// <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;
+            commandBehavior = CommandBehavior.Default;
+            
+            parameters = new NpgsqlParameterCollection();
+            timeout = 20;
+        }
 
         // Public properties.
         /// <summary>
@@ -130,6 +150,7 @@ namespace Npgsql
                 planName = String.Empty;
                 parse = null;
                 bind = null;
+                commandBehavior = CommandBehavior.Default;
             }
         }
 
@@ -175,7 +196,8 @@ namespace Npgsql
             }
         }
 
-        IDbConnection IDbCommand.Connection {
+        IDbConnection IDbCommand.Connection 
+        {
             get
             {
                 return Connection;
@@ -183,7 +205,7 @@ namespace Npgsql
 
             set
             {
-                connection = (NpgsqlConnection) value;
+                Connection = (NpgsqlConnection) value;
                 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Connection", value);
             }
         }
@@ -203,15 +225,35 @@ namespace Npgsql
 
             set
             {
-                if (this.transaction != null && this.transaction.Connection == null)
-                    this.transaction = null;
-                if (this.connection != null && this.connection.InTransaction == true)
+                if (this.Connection == value)
+                    return;
+
+                //if (this.transaction != null && this.transaction.Connection == null)
+                  //  this.transaction = null;
+                                    
+                if (this.transaction != null && this.connection != null && this.Connector.Transaction != null)
                     throw new InvalidOperationException(resman.GetString("Exception_SetConnectionInTransaction"));
+
+
                 this.connection = value;
+                Transaction = null;
+                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
             {
@@ -223,7 +265,10 @@ namespace Npgsql
         /// Gets the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>.
         /// </summary>
         /// <value>The parameters of the SQL statement or function (stored procedure). The default is an empty collection.</value>
+        #if WITHDESIGN
         [Category("Data"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+        #endif
+        
         public NpgsqlParameterCollection Parameters {
             get
             {
@@ -232,14 +277,32 @@ namespace Npgsql
             }
         }
 
+        
+        IDbTransaction IDbCommand.Transaction 
+        {
+            get
+            {
+                return Transaction;
+            }
+
+            set
+            {
+                Transaction = (NpgsqlTransaction) value;
+                NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Transaction", value);
+            }
+        }
+        
         /// <summary>
         /// Gets or sets the <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
         /// within which the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> executes.
         /// </summary>
         /// <value>The <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>.
         /// The default value is a null reference.</value>
+        #if WITHDESIGN
         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
-        public IDbTransaction Transaction {
+        #endif
+        
+        public NpgsqlTransaction Transaction {
             get
             {
                 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Transaction");
@@ -265,7 +328,10 @@ namespace Npgsql
         /// method of the <see cref="System.Data.Common.DbDataAdapter">DbDataAdapter</see>.
         /// </summary>
         /// <value>One of the <see cref="System.Data.UpdateRowSource">UpdateRowSource</see> values.</value>
+        #if WITHDESIGN
         [Category("Behavior"), DefaultValue(UpdateRowSource.Both)]
+        #endif
+        
         public UpdateRowSource UpdatedRowSource {
             get
             {
@@ -292,6 +358,26 @@ namespace Npgsql
             // [TODO] Finish method implementation.
             throw new NotImplementedException();
         }
+        
+        /// <summary>
+        /// Create a new command based on this one.
+        /// </summary>
+        /// <returns>A new NpgsqlCommand object.</returns>
+        Object ICloneable.Clone()
+        {
+            return Clone();
+        }
+
+        /// <summary>
+        /// Create a new connection based on this one.
+        /// </summary>
+        /// <returns>A new NpgsqlConnection object.</returns>
+        public NpgsqlCommand Clone()
+        {
+            // TODO: Add consistency checks.
+
+            return new NpgsqlCommand(CommandText, Connection, Transaction);
+        }
 
         /// <summary>
         /// Creates a new instance of an <see cref="System.Data.IDbDataParameter">IDbDataParameter</see> object.
@@ -324,14 +410,18 @@ namespace Npgsql
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteNonQuery");
 
             ExecuteCommand();
-
+            
+            UpdateOutputParameters();
+            
+            
             // 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;
@@ -354,6 +444,80 @@ namespace Npgsql
             else
                 return -1;
         }
+        
+        
+        
+        private void UpdateOutputParameters()
+        {
+            // Check if there was some resultset returned. If so, put the result in output parameters.
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "UpdateOutputParameters");
+            
+            // Get ResultSets.
+            ArrayList resultSets = Connector.Mediator.ResultSets;
+            
+            if (resultSets.Count != 0)
+            {
+                NpgsqlResultSet nrs = (NpgsqlResultSet)resultSets[0];
+                                
+                if ((nrs != null) && (nrs.Count > 0))
+                {
+                    NpgsqlAsciiRow nar = (NpgsqlAsciiRow)nrs[0];
+                    
+                    Int32 i = 0;
+                    Boolean hasMapping = false;
+                                        
+                    // First check if there is any mapping between parameter name and resultset name.
+                    // If so, just update output parameters which has mapping.
+                    
+                    foreach (NpgsqlParameter p in Parameters)
+                    {
+                        try
+                        {
+                            if (nrs.RowDescription.FieldIndex(p.ParameterName.Substring(1)) > -1)
+                            {
+                                hasMapping = true;
+                                break;
+                            }
+                        }
+                        catch(ArgumentOutOfRangeException)
+                        {}
+                    }
+                                        
+                    
+                    if (hasMapping)
+                    {
+                        foreach (NpgsqlParameter p in Parameters)
+                        {
+                            if (((p.Direction == ParameterDirection.Output) ||
+                                (p.Direction == ParameterDirection.InputOutput)) && (i < nrs.RowDescription.NumFields ))
+                            {
+                                try
+                                {
+                                    p.Value = nar[nrs.RowDescription.FieldIndex(p.ParameterName.Substring(1))];
+                                    i++;
+                                }
+                                catch(ArgumentOutOfRangeException)
+                                {}
+                            }
+                        }
+                        
+                    }
+                    else
+                        foreach (NpgsqlParameter p in Parameters)
+                        {
+                            if (((p.Direction == ParameterDirection.Output) ||
+                                (p.Direction == ParameterDirection.InputOutput)) && (i < nrs.RowDescription.NumFields ))
+                            {
+                                p.Value = nar[i];
+                                i++;
+                            }
+                        }
+                }
+                
+            }   
+            
+            
+        }
 
         /// <summary>
         /// Sends the <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> to
@@ -410,11 +574,14 @@ namespace Npgsql
             // [FIXME] No command behavior handling.
 
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader", cb);
+            commandBehavior = cb;
 
             ExecuteCommand();
+            
+            UpdateOutputParameters();
 
             // 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 +597,18 @@ 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);
+                    // DBNull or null values are returned as null.
+                    // TODO: Would it be better to remove this null special handling out of ConvertToBackend??
+                    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 +639,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,31 +670,33 @@ 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
             {
                 // Use the extended query parsing...
-                planName = "NpgsqlPlan" + System.Threading.Interlocked.Increment(ref planIndex);
-                String portalName = "NpgsqlPortal" + System.Threading.Interlocked.Increment(ref portalIndex);
+                //planName = "NpgsqlPlan" + Connector.NextPlanIndex();
+                planName = Connector.NextPlanName();
+                String portalName = Connector.NextPortalName();
 
                 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});
+                bind = new NpgsqlBind("", planName, new Int16[] {0}, null, new Int16[] {0});
             }
         }
 
@@ -559,12 +730,12 @@ namespace Npgsql
         {
             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>
@@ -588,35 +759,152 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetClearCommandText");
 
-
+            Boolean addProcedureParenthesis = false;  // Do not add procedure parenthesis by default.
+            
+            Boolean functionReturnsRecord = false;    // Functions don't return record by default.
+            
             String result = text;
 
             if (type == CommandType.StoredProcedure)
-                if (connection.SupportsPrepare)
+            {
+                
+                functionReturnsRecord = CheckFunctionReturnRecord();
+                
+                // Check if just procedure name was passed. If so, does not replace parameter names and just pass parameter values in order they were added in parameters collection.
+                if (!result.Trim().EndsWith(")"))  
+                {
+                    addProcedureParenthesis = true;
+                    result += "(";
+                }
+                
+                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.
+                    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.
+                return "select * from " + result;       // There is no parameter support on table direct.
 
-            if (parameters.Count == 0)
+            if (parameters == null || parameters.Count == 0)
+            {
+                if (addProcedureParenthesis)
+                        result += ")";
+                        
+                if (functionReturnsRecord)
+                    result = AddFunctionReturnsRecordSupport(result);
+                
+                
+                result = AddSingleRowBehaviorSupport(result);
+                                           
                 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];
 
+                
+                if ((Param.Direction == ParameterDirection.Input) ||
+                    (Param.Direction == ParameterDirection.InputOutput))
+                
+                    
+                    // If parenthesis don't need to be added, they were added by user with parameter names. Replace them.
+                    if (!addProcedureParenthesis)
+                        // FIXME DEBUG ONLY
+                        // adding the '::<datatype>' on the end of a parameter is a highly
+                        // questionable practice, but it is great for debugging!
+                        // Removed as this was going in infinite loop when the parameter name had the same name of parameter
+                        // type name. i.e.: parameter name called :text of type text. It would conflict with the parameter type name ::text.
+                        result = ReplaceParameterValue(
+                                    result,
+                                    Param.ParameterName,
+                                    Param.TypeInfo.ConvertToBackend(Param.Value, false)
+                                );
+                    else
+                        result += Param.TypeInfo.ConvertToBackend(Param.Value, false) + ",";
+            }
+            
+            
+            if (addProcedureParenthesis)
+            {
+                // Remove a trailing comma added from parameter handling above. If any.
+                // Maybe there are only output parameters.
+                if (result.EndsWith(","))
+                    result = result.Remove(result.Length - 1, 1);
+                
+                result += ")";
             }
 
+            if (functionReturnsRecord)
+                result = AddFunctionReturnsRecordSupport(result);
+                
+            return AddSingleRowBehaviorSupport(result);
+        }
+        
+        
+        
+        private Boolean CheckFunctionReturnRecord()
+        {
+        
+            if (Parameters.Count == 0)
+                return false;
+                
+            String returnRecordQuery = "select count(*) > 0 from pg_proc where prorettype = ( select oid from pg_type where typname = 'record' ) and proargtypes='{0}' and proname='{1}';";
+            
+            StringBuilder parameterTypes = new StringBuilder("");
+            
+            foreach(NpgsqlParameter p in Parameters)
+            {
+                if ((p.Direction == ParameterDirection.Input) ||
+                (p.Direction == ParameterDirection.InputOutput))
+                {
+                    parameterTypes.Append(Connection.Connector.OidToNameMapping[p.TypeInfo.Name].OID + " ");
+                }
+            }
+        
+                
+            NpgsqlCommand c = new NpgsqlCommand(String.Format(returnRecordQuery, parameterTypes.ToString(), CommandText), Connection);
+            
+            Boolean ret = (Boolean) c.ExecuteScalar();
+            
+            // reset any responses just before getting new ones
+            connector.Mediator.ResetResponses();
+            return ret;
+            
+        
+        }
+        
+        
+        private String AddFunctionReturnsRecordSupport(String OriginalResult)
+        {
+                                
+            StringBuilder sb = new StringBuilder(OriginalResult);
+            
+            sb.Append(" as (");
+            
+            foreach(NpgsqlParameter p in Parameters)
+            {
+                if ((p.Direction == ParameterDirection.Output) ||
+                (p.Direction == ParameterDirection.InputOutput))
+                {
+                    sb.Append(String.Format("{0} {1}, ", p.ParameterName.Substring(1), p.TypeInfo.Name));
+                }
+            }
+            
+            String result = sb.ToString();
+            
+            result = result.Remove(result.Length - 2, 1);
+            
+            result += ")";
+            
+            
+            
             return result;
-
+            
+            
         }
 
 
@@ -634,7 +922,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);
@@ -650,10 +938,21 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetParseCommandText");
 
+            Boolean addProcedureParenthesis = false;  // Do not add procedure parenthesis by default.
+            
             String parseCommand = text;
 
             if (type == CommandType.StoredProcedure)
+            {
+                // Check if just procedure name was passed. If so, does not replace parameter names and just pass parameter values in order they were added in parameters collection.
+                if (!parseCommand.Trim().EndsWith(")"))  
+                {
+                    addProcedureParenthesis = true;
+                    parseCommand += "(";
+                }
+                
                 parseCommand = "select * from " + parseCommand; // This syntax is only available in 7.3+ as well SupportsPrepare.
+            }
             else if (type == CommandType.TableDirect)
                 return "select * from " + parseCommand; // There is no parameter support on TableDirect.
 
@@ -666,15 +965,28 @@ namespace Npgsql
 
                 for (i = 0; i < parameters.Count; i++)
                 {
-                    //result = result.Replace(":" + parameterName, parameters[i].Value.ToString());
-                    parameterName = parameters[i].ParameterName;
-                    //textCommand = textCommand.Replace(':' + parameterName, "$" + (i+1));
-                    parseCommand = ReplaceParameterValue(parseCommand, parameterName, "$" + (i+1));
+                    if ((parameters[i].Direction == ParameterDirection.Input) ||
+                    (parameters[i].Direction == ParameterDirection.InputOutput))
+                    {
+                    
+                        if (!addProcedureParenthesis)
+                        {
+                            //result = result.Replace(":" + parameterName, parameters[i].Value.ToString());
+                            parameterName = parameters[i].ParameterName;
+                            //textCommand = textCommand.Replace(':' + parameterName, "$" + (i+1));
+                            parseCommand = ReplaceParameterValue(parseCommand, parameterName, "$" + (i+1) + "::" + parameters[i].TypeInfo.Name);
+                        }
+                        else
+                            parseCommand += "$" + (i+1) + "::" + parameters[i].TypeInfo.Name;
+                    }
 
                 }
             }
 
-            return parseCommand;
+            if (addProcedureParenthesis)
+                return parseCommand + ")";
+            else
+                return parseCommand;
 
         }
 
@@ -683,16 +995,25 @@ namespace Npgsql
         {
             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetPrepareCommandText");
 
+            Boolean addProcedureParenthesis = false;  // Do not add procedure parenthesis by default.
 
-
-            planName = "NpgsqlPlan" + System.Threading.Interlocked.Increment(ref planIndex);
+            planName = Connector.NextPlanName();
 
             StringBuilder command = new StringBuilder("prepare " + planName);
 
             String textCommand = text;
 
             if (type == CommandType.StoredProcedure)
+            {
+                // Check if just procedure name was passed. If so, does not replace parameter names and just pass parameter values in order they were added in parameters collection.
+                if (!textCommand.Trim().EndsWith(")"))  
+                {
+                    addProcedureParenthesis = true;
+                    textCommand += "(";
+                }
+                
                 textCommand = "select * from " + textCommand;
+            }
             else if (type == CommandType.TableDirect)
                 return "select * from " + textCommand; // There is no parameter support on TableDirect.
 
@@ -706,12 +1027,22 @@ namespace Npgsql
 
                 for (i = 0; i < parameters.Count; i++)
                 {
-                    //result = result.Replace(":" + parameterName, parameters[i].Value.ToString());
-                    parameterName = parameters[i].ParameterName;
-                    // The space in front of '$' fixes a parsing problem in 7.3 server
-                    // which gives errors of operator when finding the caracters '=$' in
-                    // prepare text
-                    textCommand = ReplaceParameterValue(textCommand, parameterName, " $" + (i+1));
+                    if ((parameters[i].Direction == ParameterDirection.Input) ||
+                    (parameters[i].Direction == ParameterDirection.InputOutput))
+                    {
+                    
+                        if (!addProcedureParenthesis)
+                        {
+                            //result = result.Replace(":" + parameterName, parameters[i].Value.ToString());
+                            parameterName = parameters[i].ParameterName;
+                            // The space in front of '$' fixes a parsing problem in 7.3 server
+                            // which gives errors of operator when finding the caracters '=$' in
+                            // prepare text
+                            textCommand = ReplaceParameterValue(textCommand, parameterName, " $" + (i+1));
+                        }
+                        else
+                            textCommand += " $" + (i+1);
+                    }
 
                 }
 
@@ -722,7 +1053,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(',');
                 }
@@ -731,6 +1063,9 @@ namespace Npgsql
                 command.Append(')');
 
             }
+            
+            if (addProcedureParenthesis)
+                textCommand += ")";
 
             command.Append(" as ");
             command.Append(textCommand);
@@ -749,13 +1084,17 @@ namespace Npgsql
             Int32 paramEnd = paramStart + paramLen;
             Boolean found = false;
 
+
             while(paramStart > -1)
             {
                 if((resLen > paramEnd) &&
                         (result[paramEnd] == ' ' ||
                          result[paramEnd] == ',' ||
                          result[paramEnd] == ')' ||
-                         result[paramEnd] == ';'))
+                         result[paramEnd] == ';' ||
+                         result[paramEnd] == '\n' ||
+                         result[paramEnd] == '\r' ||
+                         result[paramEnd] == '\t'))
                 {
                     result = result.Substring(0, paramStart) + paramVal + result.Substring(paramEnd);
                     found = true;
@@ -775,8 +1114,29 @@ namespace Npgsql
             if(!found)
                 throw new IndexOutOfRangeException (String.Format(resman.GetString("Exception_ParamNotInQuery"), parameterName));
 
+
             return result;
         }//ReplaceParameterValue
+        
+        
+        private String AddSingleRowBehaviorSupport(String ResultCommandText)
+        {
+            
+            ResultCommandText = ResultCommandText.Trim();
+        
+            if ((commandBehavior & CommandBehavior.SingleRow) > 0)
+            {
+                if (ResultCommandText.EndsWith(";"))
+                    ResultCommandText = ResultCommandText.Substring(0, ResultCommandText.Length - 1);
+                ResultCommandText += " limit 1;";
+                
+            }
+            
+            
+            
+            return ResultCommandText;
+            
+        }
 
 
         private void ExecuteCommand()
@@ -784,27 +1144,40 @@ namespace Npgsql
             // Check the connection state first.
             CheckConnectionState();
 
-            if (parse == null) {
-                connection.Query(this);
+            // reset any responses just before getting new ones
+            connector.Mediator.ResetResponses();
 
-                // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
-            } else {
-                BindParameters();
 
-                // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
-
-                connection.Execute(new NpgsqlExecute(bind.PortalName, 0));
+            if (parse == null)
+            {
+                Connector.Query(this);
 
                 // Check for errors and/or notifications and do the Right Thing.
-                connection.CheckErrorsAndNotifications();
+                connector.CheckErrorsAndNotifications();
             }
-            /* else
-                       throw new NotImplementedException(resman.GetString("Exception_CommandTypeTableDirect"));*/
-        }
+            else
+            {
+                try
+                {
 
+                    BindParameters();
 
-    }
+                    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();
+                }
+            }
+        }
+    }
 }