// 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
/// <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>
planName = String.Empty;
parse = null;
bind = null;
+ commandBehavior = CommandBehavior.Default;
}
}
set
{
if (value < 0)
- throw new ArgumentException(resman.GetString("Exception_CommandTimeoutLessZero"));
+ throw new ArgumentOutOfRangeException(resman.GetString("Exception_CommandTimeoutLessZero"));
timeout = value;
NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "CommandTimeout", value);
type = value;
NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "CommandType", value);
}
-
}
- IDbConnection IDbCommand.Connection {
+ IDbConnection IDbCommand.Connection
+ {
get
{
return Connection;
set
{
- connection = (NpgsqlConnection) value;
+ Connection = (NpgsqlConnection) value;
NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Connection", value);
}
}
set
{
- if (this.transaction != null && this.transaction.Connection == null)
- this.transaction = null;
- if (this.connection != null && this.connection.InTransaction == true)
- throw new NpgsqlException(resman.GetString("Exception_SetConnectionInTransaction"));
+ 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
{
/// 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
{
}
}
+
+ 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");
- //throw new NotImplementedException();
+
if (this.transaction != null && this.transaction.Connection == null)
{
this.transaction = null;
set
{
NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Transaction" ,value);
- //throw new NotImplementedException();
- /*if (this.connection != null && this.connection.InTransaction == true){
- throw new NpgsqlException(resman.GetString("Exception_SetTransactionInTransaction"));
- }*/
+
this.transaction = (NpgsqlTransaction) value;
}
}
/// 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
{
NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "UpdatedRowSource");
- // [FIXME] Strange, the line below doesn't appears in the stack trace.
- //throw new NotImplementedException();
return UpdateRowSource.Both;
}
}
}
- private void CheckNotification()
- {
- if (connection.Mediator.Notifications.Count > 0)
- for (int i=0; i < connection.Mediator.Notifications.Count; i++)
- connection.Notify((NpgsqlNotificationEventArgs) connection.Mediator.Notifications[i]);
-
- }
-
/// <summary>
/// Attempts to cancel the execution of a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
/// </summary>
// [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.
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteNonQuery");
- // Check the connection state.
- CheckConnectionState();
-
ExecuteCommand();
-
- // Check if there were any errors.
- if (connection.Mediator.Errors.Count > 0)
- {
- StringWriter sw = new StringWriter();
- sw.WriteLine(String.Format(resman.GetString("Exception_MediatorErrors"), "ExecuteNonQuery"));
- uint i = 1;
- foreach(string error in connection.Mediator.Errors)
- {
- sw.WriteLine("{0}. {1}", i++, error);
- }
- throw new NpgsqlException(sw.ToString());
- }
-
- CheckNotification();
-
-
-
- // The only expected result is the CompletedResponse result.
+
+ UpdateOutputParameters();
+
+
// If nothing is returned, just return -1.
-
- if(connection.Mediator.GetCompletedResponses().Count == 0)
+ if(Connector.Mediator.CompletedResponses.Count == 0)
+ {
return -1;
+ }
-
// Check if the response is available.
- String firstCompletedResponse = (String)connection.Mediator.GetCompletedResponses()[0];
-
+ String firstCompletedResponse = (String)Connector.Mediator.CompletedResponses[0];
+
if (firstCompletedResponse == null)
return -1;
-
+
String[] ret_string_tokens = firstCompletedResponse.Split(null); // whitespace separator.
// Only theses commands return rows affected.
// [FIXME] Is there a better way to check this??
if ((String.Compare(ret_string_tokens[0], "INSERT", true) == 0) ||
- (String.Compare(ret_string_tokens[0], "UPDATE", true) == 0) ||
- (String.Compare(ret_string_tokens[0], "DELETE", true) == 0))
+ (String.Compare(ret_string_tokens[0], "UPDATE", true) == 0) ||
+ (String.Compare(ret_string_tokens[0], "DELETE", true) == 0))
// The number of rows affected is in the third token for insert queries
// and in the second token for update and delete queries.
return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]);
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>
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.ExecuteReader", cb);
return (NpgsqlDataReader) ExecuteReader(cb);
-
}
/// <summary>
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader");
-
return ExecuteReader(CommandBehavior.Default);
-
}
/// <summary>
// [FIXME] No command behavior handling.
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader", cb);
-
- // Check the connection state.
- CheckConnectionState();
-
+ commandBehavior = cb;
ExecuteCommand();
-
- // Check if there were any errors.
- if (connection.Mediator.Errors.Count > 0)
- {
- StringWriter sw = new StringWriter();
- sw.WriteLine(String.Format(resman.GetString("Exception_MediatorErrors_1P"), "ExecuteReader", cb));
- uint i = 1;
- foreach(string error in connection.Mediator.Errors)
- {
- sw.WriteLine("{0}. {1}", i++, error);
- }
- throw new NpgsqlException(sw.ToString());
- }
-
- CheckNotification();
-
+ UpdateOutputParameters();
+
// Get the resultsets and create a Datareader with them.
- return new NpgsqlDataReader(connection.Mediator.GetResultSets(), connection.Mediator.GetCompletedResponses(), connection, cb);
+ return new NpgsqlDataReader(Connector.Mediator.ResultSets, Connector.Mediator.CompletedResponses, connection, cb);
}
-
///<summary>
/// This method binds the parameters from parameters collection to the bind
/// message.
Object[] parameterValues = new Object[parameters.Count];
for (Int32 i = 0; i < parameters.Count; i++)
{
- parameterValues[i] = NpgsqlTypesHelper.ConvertNpgsqlParameterToBackendStringValue(parameters[i]);
+ // Do not quote strings, or escape existing quotes - this will be handled by the backend.
+ // 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.Flush();
+ Connector.Bind(bind);
+ Connector.Mediator.RequireReadyForQuery = false;
+ Connector.Flush();
+ connector.CheckErrorsAndNotifications();
}
-
/// <summary>
/// Executes the query, and returns the first column of the first row
/// in the result set returned by the query. Extra columns or rows are ignored.
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteScalar");
-
- // Check the connection state.
- CheckConnectionState();
-
/*if ((type == CommandType.Text) || (type == CommandType.StoredProcedure))
if (parse == null)
connection.Query(this);
ExecuteCommand();
- // Check if there were any errors.
- // [FIXME] Just check the first error.
- if (connection.Mediator.Errors.Count > 0)
- {
- StringWriter sw = new StringWriter();
- sw.WriteLine(String.Format(resman.GetString("Exception_MediatorErrors"), "ExecuteScalar"));
- uint i = 1;
- foreach(string error in connection.Mediator.Errors)
- {
- sw.WriteLine("{0}. {1}", i++, error);
- }
- throw new NpgsqlException(sw.ToString());
- }
-
- CheckNotification();
-
- //ArrayList results = connection.Mediator.Data;
-
- //Object result = null; // Result of the ExecuteScalar().
-
-
// Now get the results.
// Only the first column of the first row must be returned.
// Get ResultSets.
- ArrayList resultSets = connection.Mediator.GetResultSets();
-
+ ArrayList resultSets = Connector.Mediator.ResultSets;
// First data is the RowDescription object.
// Check all resultsets as insert commands could have been sent along
- // with resultset queries. The insert commands return null, so, if
- // we find one, skip to next resultset. If no resultset is found, return null
- // as per specification.
-
+ // with resultset queries. The insert commands return null and and some queries
+ // may return empty resultsets, so, if we find one of these, skip to next resultset.
+ // If no resultset is found, return null as per specification.
+
NpgsqlAsciiRow ascii_row = null;
- foreach( Object rs in resultSets )
+ foreach( NpgsqlResultSet nrs in resultSets )
{
- if( rs != null )
+ if( (nrs != null) && (nrs.Count > 0) )
{
- NpgsqlResultSet nrs = (NpgsqlResultSet) rs;
ascii_row = (NpgsqlAsciiRow) nrs[0];
return ascii_row[0];
}
}
-
-
- return null;
-
+ return null;
}
/// <summary>
// Check the connection state.
CheckConnectionState();
- if (!connection.SupportsPrepare)
+ if (! Connector.SupportsPrepare)
+ {
return; // Do nothing.
+ }
-
-
- // [TODO] Finish method implementation.
- //throw new NotImplementedException();
-
- //NpgsqlCommand command = new NpgsqlCommand("prepare plan1 as " + GetCommandText(), connection );
-
- 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.Flush();
+ Connector.Parse(parse);
+ Connector.Mediator.RequireReadyForQuery = false;
+ Connector.Flush();
- bind = new NpgsqlBind(portalName, planName, new Int16[] {0}, null, new Int16[] {0});
+ // Check for errors and/or notifications and do the Right Thing.
+ connector.CheckErrorsAndNotifications();
+ bind = new NpgsqlBind("", planName, new Int16[] {0}, null, new Int16[] {0});
}
}
+ /*
/// <summary>
/// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
/// </summary>
- /*protected override void Dispose (bool disposing)
+ protected override void Dispose (bool disposing)
{
if (disposing)
{
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>
return GetClearCommandText();
else
return GetPreparedCommandText();
-
-
}
{
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]));
+ 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;
-
+
+
}
for (Int32 i = 0; i < parameters.Count; i++)
{
- result.Append(NpgsqlTypesHelper.ConvertNpgsqlParameterToBackendStringValue(parameters[i]) + ',');
+ result.Append(parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, false) + ',');
}
result = result.Remove(result.Length - 1, 1);
{
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.
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;
}
{
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.
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);
+ }
}
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(',');
}
command.Append(')');
}
+
+ if (addProcedureParenthesis)
+ textCommand += ")";
command.Append(" as ");
command.Append(textCommand);
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;
}//while
if(!found)
- throw new NpgsqlException(String.Format(resman.GetString("Exception_ParamNotInQuery"), parameterName));
+ 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()
{
- //if ((type == CommandType.Text) || (type == CommandType.StoredProcedure))
+ // Check the connection state first.
+ CheckConnectionState();
+
+ // reset any responses just before getting new ones
+ connector.Mediator.ResetResponses();
+
+
if (parse == null)
- connection.Query(this);
- else
{
- BindParameters();
- connection.Execute(new NpgsqlExecute(bind.PortalName, 0));
+ Connector.Query(this);
+
+ // Check for errors and/or notifications and do the Right Thing.
+ 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();
+ }
+ }
+ }
+ }
}