// Logging related values
private static readonly String CLASSNAME = "NpgsqlAsciiRow";
- private ArrayList data;
- private readonly Int16 READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
- private NpgsqlRowDescription row_desc;
- private Hashtable oid_to_name_mapping;
- private Int32 protocol_version;
+ private ArrayList data;
+ private readonly Int16 READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
+ private NpgsqlRowDescription row_desc;
+ private Hashtable oid_to_name_mapping;
+ private ProtocolVersion protocol_version;
- public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, Hashtable oidToNameMapping, Int32 protocolVersion)
+ public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, Hashtable oidToNameMapping, ProtocolVersion protocolVersion)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
row_desc = rowDesc;
oid_to_name_mapping = oidToNameMapping;
protocol_version = protocolVersion;
-
}
public void ReadFromStream(Stream inputStream, Encoding encoding)
{
- if (protocol_version == ProtocolVersion.Version2)
- {
+ switch (protocol_version) {
+ case ProtocolVersion.Version2 :
ReadFromStream_Ver_2(inputStream, encoding);
- }
- else
- {
+ break;
+
+ case ProtocolVersion.Version3 :
ReadFromStream_Ver_3(inputStream, encoding);
+ break;
+
}
}
private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding)
{
- NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2()");
+ 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];
private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding)
{
- NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3()");
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
Byte[] input_buffer = new Byte[READ_BUFFER_SIZE];
private Int32 _processId;
private Int32 _secretKey;
- private Int32 _protocolVersion;
+ private ProtocolVersion _protocolVersion;
- public NpgsqlBackEndKeyData(Int32 protocolVersion)
+ public NpgsqlBackEndKeyData(ProtocolVersion protocolVersion)
{
_protocolVersion = protocolVersion;
_processId = -1;
{
/// <summary>
- /// This class represents the AsciiRow message sent from PostgreSQL
- /// server.
+ /// This class represents the BinaryRow message sent from PostgreSQL
+ /// server. This is unused as of protocol version 3.
/// </summary>
///
internal sealed class NpgsqlBinaryRow
// Logging related values
private static readonly String CLASSNAME = "NpgsqlBinaryRow";
- private ArrayList data;
- // private readonly Int16 READ_BUFFER_SIZE = 300; //[FIXME] Is this enough??
- private NpgsqlRowDescription row_desc;
+ private ArrayList data;
+ private NpgsqlRowDescription row_desc;
public NpgsqlBinaryRow(NpgsqlRowDescription rowDesc)
{
data = new ArrayList();
row_desc = rowDesc;
-
}
// The only expected result is the CompletedResponse result.
// If nothing is returned, just return -1.
- if(connection.Mediator.GetCompletedResponses().Count == 0)
+ if(connection.Mediator.CompletedResponses.Count == 0)
return -1;
// Check if the response is available.
- String firstCompletedResponse = (String)connection.Mediator.GetCompletedResponses()[0];
+ String firstCompletedResponse = (String)connection.Mediator.CompletedResponses[0];
if (firstCompletedResponse == null)
return -1;
// Get the resultsets and create a Datareader with them.
- return new NpgsqlDataReader(connection.Mediator.GetResultSets(), connection.Mediator.GetCompletedResponses(), connection, cb);
+ return new NpgsqlDataReader(connection.Mediator.ResultSets, connection.Mediator.CompletedResponses, connection, cb);
}
// Only the first column of the first row must be returned.
// Get ResultSets.
- ArrayList resultSets = connection.Mediator.GetResultSets();
+ ArrayList resultSets = connection.Mediator.ResultSets;
// First data is the RowDescription object.
private NpgsqlConnectedState()
{}
+
public static NpgsqlConnectedState Instance
{
get
return _instance;
}
}
+
public override void Startup(NpgsqlConnection context)
{
- if (context.BackendProtocolVersion == ProtocolVersion.Version3)
- {
-
- NpgsqlStartupPacket startupPacket = new NpgsqlStartupPacket(296, //Not used.
- 3,
- 0,
- context.DatabaseName,
- context.UserName,
- "",
- "",
- "");
-
- startupPacket.WriteToStream( new BufferedStream(context.Stream), context.Encoding );
- ProcessBackendResponses( context );
- }
- else if (context.BackendProtocolVersion == ProtocolVersion.Version2)
- {
-
- NpgsqlStartupPacket startupPacket = new NpgsqlStartupPacket(296,
- 2,
- 0,
- context.DatabaseName,
- context.UserName,
- "",
- "",
- "");
-
- startupPacket.WriteToStream( new BufferedStream(context.Stream), context.Encoding );
- ProcessBackendResponses( context );
-
-
- }
+ NpgsqlStartupPacket startupPacket = new NpgsqlStartupPacket(296, //Not used.
+ context.BackendProtocolVersion,
+ context.DatabaseName,
+ context.UserName,
+ "",
+ "",
+ "");
+ startupPacket.WriteToStream( new BufferedStream(context.Stream), context.Encoding );
+ ProcessBackendResponses( context );
}
}
[System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))]
public sealed class NpgsqlConnection : Component, IDbConnection, ICloneable
{
+ // Logging related values
+ private readonly String CLASSNAME = "NpgsqlConnection";
+
//Changed the Name of this event because events usually don't start with 'On' in the .Net-Framework
// (but their handlers do ;-)
/// <summary>
/// Occurs on NotificationResponses from the PostgreSQL backend.
/// </summary>
- public event NotificationEventHandler Notification;
-
+ public event NotificationEventHandler Notification;
// Public properties for ssl callbacks
- public CertificateValidationCallback CertificateValidationCallback;
- public CertificateSelectionCallback CertificateSelectionCallback;
- public PrivateKeySelectionCallback PrivateKeySelectionCallback;
-
+ public CertificateValidationCallback CertificateValidationCallback;
+ public CertificateSelectionCallback CertificateSelectionCallback;
+ public PrivateKeySelectionCallback PrivateKeySelectionCallback;
+ private NpgsqlState state;
- private NpgsqlState state;
-
- private ConnectionState connection_state;
- private String connection_string;
- internal ListDictionary connection_string_values;
+ private ConnectionState connection_state;
+ private String connection_string;
+ internal ListDictionary connection_string_values;
// some of the following constants are needed
// for designtime support so I made them 'internal'
// as I didn't want to add another interface for internal access
// Values for possible CancelRequest messages.
- private NpgsqlBackEndKeyData backend_keydata;
+ private NpgsqlBackEndKeyData backend_keydata;
// Flag for transaction status.
- private Boolean _inTransaction = false;
+ private Boolean _inTransaction = false;
// Mediator which will hold data generated from backend
- private NpgsqlMediator _mediator;
-
- // Logging related values
- private readonly String CLASSNAME = "NpgsqlConnection";
-
- private Stream stream;
+ private NpgsqlMediator _mediator;
+ private Stream stream;
+ private Connector _connector;
+ private Encoding connection_encoding;
- private Connector _connector;
+ private ServerVersion _serverVersion;
+ private ProtocolVersion _backendProtocolVersion;
+ private Int32 _connectionTimeout;
- private Encoding connection_encoding;
+ private Boolean _supportsPrepare = false;
- private Boolean _supportsPrepare = false;
-
- private String _serverVersion; // Contains string returned from select version();
-
- private Hashtable _oidToNameMapping;
+ private Hashtable _oidToNameMapping;
private System.Resources.ResourceManager resman;
- private Int32 _backendProtocolVersion;
-
- private Int32 _connectionTimeout;
-
-
/// <summary>
/// Initializes a new instance of the
/// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class.
connection_state = ConnectionState.Closed;
state = NpgsqlClosedState.Instance;
connection_string = ConnectionString;
- connection_string_values = new ListDictionary();
+ connection_string_values = new ListDictionary(CaseInsensitiveComparer.Default);
connection_encoding = Encoding.Default;
_backendProtocolVersion = ProtocolVersion.Version3;
CertificateValidationCallback = new CertificateValidationCallback(DefaultCertificateValidationCallback);
- if (connection_string != String.Empty)
- ParseConnectionString();
+ ParseAndSetConnectionString(ConnectionString);
}
-
/// <summary>
/// Gets or sets the string used to open a SQL Server database.
/// </summary>
set
{
NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value);
- connection_string = value;
- if (connection_string != String.Empty)
- ParseConnectionString();
+ ParseAndSetConnectionString(value);
}
}
if(this.connection_state != ConnectionState.Open)
throw new InvalidOperationException(resman.GetString("Exception_ChangeDatabaseOnOpenConn"));
- String oldDatabaseName = (String)connection_string_values[CONN_DATABASE];
+ String oldDatabaseName = ConnectStringValueToString(CONN_DATABASE);
Close();
connection_string_values[CONN_DATABASE] = dbName;
throw new InvalidOperationException(resman.GetString("Exception_ConnStrEmpty"));
if (connection_string_values[CONN_SERVER] == null)
throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), CONN_SERVER);
- if ((connection_string_values[CONN_USERID] == null) & (connection_string_values[ODBC_USERID] == null))
+ if (connection_string_values[CONN_USERID] == null)
throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), CONN_USERID);
- if ((connection_string_values[CONN_PASSWORD] == null) & (connection_string_values[ODBC_PASSWORD] == null))
+ if (connection_string_values[CONN_PASSWORD] == null)
throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), CONN_PASSWORD);
+
+ // Check and use defaults for these missing arguments.
if (connection_string_values[CONN_DATABASE] == null)
// Database is optional. "[...] defaults to the user name if empty"
connection_string_values[CONN_DATABASE] = connection_string_values[CONN_USERID];
try
{
+ String ServerVersionString = String.Empty;
// Check if the connection is already open.
if (connection_state == ConnectionState.Open)
lock(ConnectorPool.ConnectorPoolMgr)
{
Connector = ConnectorPool.ConnectorPoolMgr.RequestConnector(ConnectionString,
- Int32.Parse((String)connection_string_values[MAX_POOL_SIZE]),
- Int32.Parse((String)connection_string_values[CONN_TIMEOUT]),
+ ConnectStringValueToInt32(MAX_POOL_SIZE),
+ ConnectStringValueToInt32(CONN_TIMEOUT),
false);
Connector.InUse = true;
}
if (!Connector.IsInitialized)
{
-
// Reset state to initialize new connector in pool.
CurrentState = NpgsqlClosedState.Instance;
}
// Keep checking for errors...
- if(_mediator.Errors.Count > 0)
+ if(_mediator.Errors.Count > 0) {
throw new NpgsqlException(_mediator.Errors);
-
+ }
}
- backend_keydata = _mediator.GetBackEndKeyData();
+ backend_keydata = _mediator.BackendKeyData;
+
+ // First try to determine backend server version using the newest method.
+ try {
+ ServerVersionString = ((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]).ParameterValue;
+ } catch {}
- // Get version information to enable/disable server version features.
- // Only for protocol 2.0.
- if (BackendProtocolVersion == ProtocolVersion.Version2)
+ // Fall back to the old way, SELECT VERSION().
+ // This should not happen for protocol version 3+.
+ if (ServerVersionString.Length == 0)
{
NpgsqlCommand command = new NpgsqlCommand("select version();set DATESTYLE TO ISO;", this);
- _serverVersion = (String) command.ExecuteScalar();
+ ServerVersionString = ExtractServerVersion( (String)command.ExecuteScalar() );
}
+ // Cook version string so we can use it for enabling/disabling things based on
+ // backend version.
+ _serverVersion = ParseServerVersion(ServerVersionString);
+
// Adjust client encoding.
//NpgsqlCommand commandEncoding = new NpgsqlCommand("show client_encoding", this);
//String clientEncoding = (String)commandEncoding.ExecuteScalar();
- if (connection_string_values[CONN_ENCODING].Equals("UNICODE"))
+ if (ConnectStringValueToString(CONN_ENCODING).ToUpper() == "UNICODE") {
connection_encoding = Encoding.UTF8;
-
+ }
Connector.ServerVersion = ServerVersion;
Connector.BackendProtocolVersion = BackendProtocolVersion;
// Do a mini initialization in the state machine.
connection_state = ConnectionState.Open;
+
ServerVersion = Connector.ServerVersion;
BackendProtocolVersion = Connector.BackendProtocolVersion;
Encoding = Connector.Encoding;
ProcessServerVersion();
_oidToNameMapping = NpgsqlTypesHelper.LoadTypesMapping(this);
-
-
-
-
}
catch(IOException e)
return new NpgsqlConnection(ConnectionString);
}
+
+ //
// Private util methods
+ //
/// <summary>
- /// This method parses the connection string.
+ /// This method parses, cleans, and assigns the connection string.
/// It translates it to a list of key-value pairs.
/// Valid values are:
/// Server - Address/Name of Postgresql Server
/// MaxPoolSize - Max size of connection pool
/// Encoding - Encoding to be used
/// Timeout - Time to wait for connection open. In seconds.
+ /// The resulting cleaned connection string will have all key names
+ /// upper-cased for consistency and to help ensure proper operation
+ /// with the connection pool (which is keyed on connection string).
+ /// If any errors occur, the entire operation is aborted and the
+ /// connection state will be left unchanged.
/// </summary>
- private void ParseConnectionString()
+ private void ParseAndSetConnectionString(String CS)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ParseConnectionString");
- connection_string_values.Clear();
+ ListDictionary new_values = new ListDictionary(CaseInsensitiveComparer.Default);
+ StringBuilder CleanedConnectionString = new StringBuilder();
+ String[] pairs;
+ String[] keyvalue;
// Get the key-value pairs delimited by CONN_DELIM
- String[] pairs = connection_string.Split(new Char[] {CONN_DELIM});
+ pairs = CS.Split(new Char[] {CONN_DELIM});
- String[] keyvalue;
// Now, for each pair, get its key-value.
- foreach(String s in pairs)
+ foreach(String sraw in pairs)
{
- // This happen when there are trailling/empty CONN_DELIMs
+ String s = sraw.Trim();
+ String Key = "", Value = "";
+
+ // This happens when there are trailing/empty CONN_DELIMs
// Just ignore them.
- if (s == "")
+ if (s == "") {
continue;
+ }
- keyvalue = s.Split(new Char[] {CONN_ASSIGN});
+ // Split this chunk on the first CONN_ASSIGN only.
+ keyvalue = s.Split(new Char[] {CONN_ASSIGN}, 2);
+
+ // Always trim things.
+ // Keys get uppercased for a numner of reasons
+ // (but NOT to enable case insensative comparisons).
+ Key = keyvalue[0].Trim().ToUpper();
+
+ // We don't expect keys this long, and it might be about to be put
+ // in an error message, so makes sure it is a sane length.
+ if (Key.Length > 20) {
+ Key = Key.Substring(0, 20);
+ }
// Check if there is a key-value pair.
- if (keyvalue.Length != 2)
- throw new ArgumentException(resman.GetString("Exception_WrongKeyVal"), connection_string);
+ if (keyvalue.Length != 2) {
+ throw new ArgumentException(resman.GetString("Exception_WrongKeyVal"), Key);
+ }
- // Shift the key to upper case, and substitute ODBC style keys
- keyvalue[0] = keyvalue[0].ToUpper();
- if (keyvalue[0] == ODBC_USERID)
- keyvalue[0] = CONN_USERID;
- if (keyvalue[0] == ODBC_PASSWORD)
- keyvalue[0] = CONN_PASSWORD;
+ // Always trim things.
+ Value = keyvalue[1].Trim();
- // Add the pair to the dictionary. The key is shifted to upper
- // case for case insensitivity.
+ // Do some ODBC related substitions
+ if (Key == ODBC_USERID) {
+ Key = CONN_USERID;
+ } else if (Key == ODBC_PASSWORD) {
+ Key = CONN_PASSWORD;
+ }
+
+ NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, Key, Value);
+
+ // Add the pair to the dictionary..
+ new_values.Add(Key, Value);
+
+ // Add the pair to the cleaned list. The key is shifted to upper case.
+ CleanedConnectionString.AppendFormat("{0}{1}{2}{3}", Key, CONN_ASSIGN, Value, CONN_DELIM);
+ }
+
+ // Finally assign the real containers from our scratch ones.
+ connection_string_values = new_values;
+ connection_string = CleanedConnectionString.ToString();
+ }
+
+ /// <summary>
+ /// This method takes a version string as returned by SELECT VERSION() and returns
+ /// a valid version string ("7.2.2" for example).
+ /// This is only needed when running protocol version 2.
+ /// This does not do any validity checks.
+ /// </summary>
+ private string ExtractServerVersion (string VersionString)
+ {
+ Int32 Start = 0, End = 0;
+
+ // find the first digit and assume this is the start of the version number
+ for ( ; Start < VersionString.Length && ! char.IsDigit(VersionString[Start]) ; Start++);
+
+ End = Start;
+
+ // read until hitting whitespace, which should terminate the version number
+ for ( ; End < VersionString.Length && ! char.IsWhiteSpace(VersionString[End]) ; End++);
+
+ return VersionString.Substring(Start, End - Start + 1);
+ }
+
+ /// <summary>
+ /// This method takes a version string ("7.4.1" for example) and produces
+ /// the required integer version numbers (7, 4, and 1).
+ /// </summary>
+ private ServerVersion ParseServerVersion (string VersionString)
+ {
+ String[] Parts;
+
+ Parts = VersionString.Split('.');
- NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, keyvalue[0], keyvalue[1]);
- connection_string_values.Add(keyvalue[0], keyvalue[1]);
+ if (Parts.Length != 3) {
+ throw new FormatException("Internal: Backend sent bad version string");
+ }
+
+ try {
+ return new ServerVersion(
+ Convert.ToInt32(Parts[0]),
+ Convert.ToInt32(Parts[1]),
+ Convert.ToInt32(Parts[2])
+ );
+ } catch (Exception E) {
+ throw new FormatException("Internal: Backend sent bad version string", E);
}
}
private void ProcessServerVersion ()
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessServerVersion");
- // FIXME: Do a better parsing of server version to avoid
- // hardcode version numbers.
- if (BackendProtocolVersion == ProtocolVersion.Version2)
- {
- // With protocol version 2, only 7.3 version supports prepare.
- SupportsPrepare = (_serverVersion.IndexOf("PostgreSQL 7.3") != -1);
- }
- else
- {
- // 3.0+ version is set by ParameterStatus message.
- // On protocol 3.0, 7.4 and above support it.
- SupportsPrepare = (_serverVersion.IndexOf("7.4") != -1)
- || (_serverVersion.IndexOf("7.5") != -1);
- }
+ SupportsPrepare = ServerVersion.GreaterOrEqual(7, 3, 0);
}
internal Stream Stream {
}
}
- internal String ServerVersion {
+ internal ServerVersion ServerVersion {
get
{
return _serverVersion;
}
- internal Int32 BackendProtocolVersion {
+ internal ProtocolVersion BackendProtocolVersion {
get
{
return _backendProtocolVersion;
}
}
- }
+ public Int32 ConnectStringValueToInt32(String Key)
+ {
+ if (! connection_string_values.Contains(Key)) {
+ return 0;
+ }
+ try {
+ return Convert.ToInt32(connection_string_values[Key]);
+ } catch (Exception E) {
+ throw new ArgumentException(resman.GetString("Exception_InvalidIntegerKeyVal"), Key, E);
+ }
+ }
+
+ public String ConnectStringValueToString(String Key)
+ {
+ if (! connection_string_values.Contains(Key)) {
+ return "";
+ }
+
+ return Convert.ToString(connection_string_values[Key]);
+ }
+ }
}
<data name="Exception_WrongKeyVal">
<value>key=value argument incorrect in ConnectionString</value>
</data>
+ <data name="Exception_InvalidIntegerKeyVal">
+ <value>expecting key=[numeric] value in ConnectionString</value>
+ </data>
<data name="Log_ConnectionStringValues">
<value>Connection string option: {0} = {1}</value>
</data>
private Stream _stream;
- // This is information about the connection
- // this connector is holding. For while only the server version is used.
- // Change later for a more generic way to keep it. (Hashtable)
- private String _serverVersion;
-
- private Int32 _backendProtocolVersion;
+ private ProtocolVersion _backendProtocolVersion;
+ private ServerVersion _serverVersion;
private Encoding _encoding;
}
}
- internal String ServerVersion
+ internal ServerVersion ServerVersion
{
get
{
}
}
- internal Int32 BackendProtocolVersion
+ internal ProtocolVersion BackendProtocolVersion
{
get
{
/// connector is to be moved to the PooledConnectors list.</value>
internal int mShareCount;
- /// <value>Private Buffer for the connection string property.</value>
- /// <remarks>Compared to the requested connection string in the
- /// ConnectorPool.RequestConnector() function.
- /// Should not be modified if physical connection is open.</remarks>
- private string mConnectString;
-
- /// <summary>Used to connect to the database server. </summary>
- public string ConnectString
- {
- get
- {
- return mConnectString;
- }
- set
- {
- if ( this.mOpen ) // uuuuugh, bad habits...
- {
- throw new InvalidOperationException( "Connection strings "
- + " cannot be modified if connection is open." );
- }
- mConnectString = value;
- }
- }
-
/// <value>Provides physical access to the server</value>
// !!! to be fixed
//private Npgsql.Socket Socket;
/// <summary>
/// Construcor, initializes the Connector object.
/// </summary>
- internal Connector( string ConnectString, bool Shared )
+ internal Connector( bool Shared )
{
- ConnectString = ConnectString;
Shared = Shared;
Pooled = true;
}
}*/
- internal Int32 GetPoolSize(String connectionString)
+ internal Int32 GetPoolSize(String PoolKey)
{
- ArrayList pool = (ArrayList)PooledConnectors[connectionString];
+ ArrayList pool = (ArrayList)PooledConnectors[PoolKey];
if (pool == null)
return 0;
else
/// Searches the shared and pooled connector lists for a
/// matching connector object or creates a new one.
/// </summary>
- /// <param name="ConnectString">used to connect to the
- /// database server</param>
+ /// <param name="PoolKey">A unique key (actually the connection string)
+ /// used to identify a connection</param>
/// <param name="Shared">Allows multiple connections
/// on a single connector. </param>
/// <returns>A pooled connector object.</returns>
- internal Npgsql.Connector RequestConnector (String connectionString,
+ internal Npgsql.Connector RequestConnector (String PoolKey,
Int32 maxPoolSize,
Int32 timeout,
Boolean shared )
// (unused) connectors are beeing searched.
- connectorPool = (ArrayList)PooledConnectors[connectionString];
+ connectorPool = (ArrayList)PooledConnectors[PoolKey];
if (connectorPool == null)
{
connectorPool = new ArrayList();
- PooledConnectors[connectionString] = connectorPool;
+ PooledConnectors[PoolKey] = connectorPool;
}
if (connectorPool.Count < maxPoolSize)
{
- connector = new Npgsql.Connector(connectionString, shared);
+ connector = new Npgsql.Connector(shared);
connectorPool.Add(connector);
// Logging related values
private static readonly String CLASSNAME = "NpgsqlError";
- private Int32 protocol_version;
+ private ProtocolVersion protocol_version;
private String _severity = "";
private String _code = "";
private String _message = "";
{}
- internal NpgsqlError(Int32 protocolVersion)
+ internal NpgsqlError(ProtocolVersion protocolVersion)
{
protocol_version = protocolVersion;
internal void ReadFromStream(Stream inputStream, Encoding encoding)
{
- if (protocol_version == ProtocolVersion.Version2)
- {
+ switch (protocol_version) {
+ case ProtocolVersion.Version2 :
ReadFromStream_Ver_2(inputStream, encoding);
- }
- else
- {
+ break;
+
+ case ProtocolVersion.Version3 :
ReadFromStream_Ver_3(inputStream, encoding);
+ break;
+
}
}
private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding)
{
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
+
String Raw;
String[] Parts;
private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding)
{
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
+
Int32 messageLength = PGUtil.ReadInt32(inputStream, new Byte[4]);
//[TODO] Would this be the right way to do?
using System;
using System.Text;
using System.Collections;
+using System.Collections.Specialized;
namespace Npgsql
{
private ArrayList _resultSets;
private ArrayList _responses;
private ArrayList _notifications;
+ private ListDictionary _parameters;
+ private NpgsqlBackEndKeyData _backend_key_data;
private NpgsqlRowDescription _rd;
private ArrayList _rows;
_resultSets = new ArrayList();
_responses = new ArrayList();
_notifications = new ArrayList();
+ _parameters = new ListDictionary(CaseInsensitiveComparer.Default);
+ _backend_key_data = null;
}
public void Reset()
_resultSets.Clear();
_responses.Clear();
_notifications.Clear();
+ _parameters.Clear();
+ _backend_key_data = null;
_rd = null;
}
+ public ArrayList ResultSets
+ {
+ get
+ {
+ return _resultSets;
+ }
+ }
+
+ public ArrayList CompletedResponses
+ {
+ get
+ {
+ return _responses;
+ }
+ }
+
public ArrayList Errors
{
get
{
return _notifications;
}
+ }
+ public ListDictionary Parameters
+ {
+ get
+ {
+ return _parameters;
+ }
+ }
+
+ public NpgsqlBackEndKeyData BackendKeyData
+ {
+ get
+ {
+ return _backend_key_data;
+ }
}
public void AddNotification(NpgsqlNotificationEventArgs data)
_notifications.Add(data);
}
-
-
public void AddCompletedResponse(String response)
{
if (_rd != null)
}
- public void AddBackendKeydata(NpgsqlBackEndKeyData keydata)
+ public void SetBackendKeydata(NpgsqlBackEndKeyData keydata)
{
- _responses.Add(keydata); //hack
+ _backend_key_data = keydata;
}
- public ArrayList GetResultSets()
+ public void AddParameterStatus(String Key, NpgsqlParameterStatus PS)
{
- return _resultSets;
+ _parameters[Key] = PS;
}
-
- public ArrayList GetCompletedResponses()
- {
- return _responses;
- }
-
- public NpgsqlBackEndKeyData GetBackEndKeyData()
- {
- return (NpgsqlBackEndKeyData)_responses[0]; //hack
- }
-
-
}
}
namespace Npgsql
{
/// <summary>
- /// Summary description for NpgsqlMessageTypes.
+ /// Class NpgsqlMessageTypes_Ver_2.
+ /// Defines PG frontend/backend protocol message types and parameters used in protocol version 2.
/// </summary>
- internal sealed class NpgsqlMessageTypes
+ internal sealed class NpgsqlMessageTypes_Ver_2
{
- private NpgsqlMessageTypes()
- {
- //
- // TODO: Add constructor logic here
- //
- }
+ private NpgsqlMessageTypes_Ver_2()
+ {}
+
+ public const Char StartupPacket = ' ';
+ public const Char Terminate = 'X';
+
public const Char AsciiRow = 'D';
- public const Char AuthenticationRequest = 'R';
+ public const Char BinaryRow = 'B';
+ public const Char AuthenticationRequest = 'R';
// specific Authentication request types
public const Int32 AuthenticationOk = 0;
public const Int32 AuthenticationKerberosV4 = 1;
public const Int32 AuthenticationSCMCredential = 6;
public const Char BackendKeyData = 'K';
- public const Char BinaryRow = 'B';
- public const Char BindComplete = '2';
public const Char CancelRequest = 'F';
public const Char CompletedResponse = 'C';
public const Char CopyDataRows = ' ';
public const Char NoticeResponse = 'N';
public const Char NotificationResponse = 'A';
- public const Char ParameterStatus = 'S';
- public const Char ParseComplete = '1';
public const Char PasswordPacket = ' ';
public const Char Query = 'Q';
public const Char ReadyForQuery = 'Z';
public const Char RowDescription = 'T';
public const Char SSLRequest = ' ';
+ }
+
+
+ /// <summary>
+ /// Class NpgsqlMessageTypes_Ver_3.
+ /// Defines PG frontend/backend protocol message types and parameters used in protocol version 3.
+ /// </summary>
+ internal sealed class NpgsqlMessageTypes_Ver_3
+ {
+ private NpgsqlMessageTypes_Ver_3()
+ {}
+
public const Char StartupPacket = ' ';
+ public const Char Termination = 'X';
+ public const Char DataRow = 'D';
+ public const Char AuthenticationRequest = 'R';
+ // specific Authentication request types
+ public const Int32 AuthenticationOk = 0;
+ public const Int32 AuthenticationKerberosV4 = 1;
+ public const Int32 AuthenticationKerberosV5 = 2;
+ public const Int32 AuthenticationClearTextPassword = 3;
+ public const Int32 AuthenticationCryptPassword = 4;
+ public const Int32 AuthenticationMD5Password = 5;
+ public const Int32 AuthenticationSCMCredential = 6;
+
+ public const Char BackendKeyData = 'K';
+ public const Char CancelRequest = 'F';
+ public const Char CompletedResponse = 'C';
+ public const Char CopyDataRows = ' ';
+ public const Char CopyInResponse = 'G';
+ public const Char CopyOutResponse = 'H';
+ public const Char EmptyQueryResponse = 'I';
+ public const Char ErrorResponse = 'E';
+ public const Char FunctionCall = 'F';
+ public const Char FunctionCallResponse = 'V';
+
+ public const Char NoticeResponse = 'N';
+ public const Char NotificationResponse = 'A';
+ public const Char ParameterStatus = 'S';
+ public const Char PasswordPacket = ' ';
+ public const Char Query = 'Q';
+ public const Char ReadyForQuery = 'Z';
+ public const Char RowDescription = 'T';
+ public const Char SSLRequest = ' ';
+
+ // extended query frontend messages
+ public const Char Parse = 'P';
+ public const Char Bind = 'B';
+ public const Char Execute = 'E';
+ public const Char Describe = 'D';
+ public const Char Close = 'C';
+ public const Char Flush = 'H';
+ public const Char Sync = 'S';
+
+ // extended query backend messages
+ public const Char ParseComplete = '1';
+ public const Char BindComplete = '2';
+ public const Char PortalSuspended = 's';
+ public const Char ParameterDescription = 't';
+ public const Char NoData = 'n';
+ public const Char CloseComplete = '3';
}
}
private static readonly String CLASSNAME = "NpgsqlPasswordPacket";
private String password;
- private Int32 protocolVersion;
+ private ProtocolVersion protocolVersion;
- public NpgsqlPasswordPacket(String password, Int32 protocolVersion)
+ public NpgsqlPasswordPacket(String password, ProtocolVersion protocolVersion)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream");
- if (protocolVersion == ProtocolVersion.Version2)
- { // Write the size of the packet.
+ switch (protocolVersion) {
+ case ProtocolVersion.Version2 :
+ // Write the size of the packet.
// 4 + (passwordlength + 1) -> Int32 + NULL terminated string.
- //output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(4 + (password.Length + 1))), 0, 4);
-
+ // output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(4 + (password.Length + 1))), 0, 4);
PGUtil.WriteInt32(outputStream, 4 + encoding.GetByteCount(password) + 1);
// Write String.
PGUtil.WriteString(password, outputStream, encoding);
- }
- else
- {
+
+ break;
+
+ case ProtocolVersion.Version3 :
outputStream.WriteByte((Byte)'p');
PGUtil.WriteInt32(outputStream, 4 + encoding.GetByteCount(password) + 1);
// Write String.
PGUtil.WriteString(password, outputStream, encoding);
- }
+ break;
+ }
}
}
-
}
internal sealed class NpgsqlQuery
{
private String _commandText;
- private Int32 _protocolVersion;
+ private ProtocolVersion _protocolVersion;
- public NpgsqlQuery(String commandText, Int32 protocolVersion)
+ public NpgsqlQuery(String commandText, ProtocolVersion protocolVersion)
{
_commandText = commandText;
_protocolVersion = protocolVersion;
// Write the byte 'Q' to identify a query message.
outputStream.WriteByte((Byte)'Q');
-
- if (_protocolVersion == ProtocolVersion.Version3)
+ if (_protocolVersion == ProtocolVersion.Version3) {
// Write message length. Int32 + string length + null terminator.
PGUtil.WriteInt32(outputStream, 4 + encoding.GetByteCount(_commandText) + 1);
-
-
-
+ }
// Write the query. In this case it is the CommandText text.
// It is a string terminated by a C NULL character.
-
PGUtil.WriteString(_commandText, outputStream, encoding);
-
-
-
}
}
}
private ArrayList fields_index = new ArrayList();
- private Int32 protocol_version;
+ private ProtocolVersion protocol_version;
- public NpgsqlRowDescription(Int32 protocolVersion)
+ public NpgsqlRowDescription(ProtocolVersion protocolVersion)
{
protocol_version = protocolVersion;
}
public void ReadFromStream(Stream input_stream, Encoding encoding)
{
- NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream");
+ switch (protocol_version) {
+ case ProtocolVersion.Version2 :
+ ReadFromStream_Ver_2(input_stream, encoding);
+ break;
+ case ProtocolVersion.Version3 :
+ ReadFromStream_Ver_3(input_stream, encoding);
+ break;
- if (protocol_version == ProtocolVersion.Version2)
- {
- Byte[] input_buffer = new Byte[10]; // Max read will be 4 + 2 + 4
+ }
+ }
- // Read the number of fields.
- input_stream.Read(input_buffer, 0, 2);
- Int16 num_fields = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 0));
+ public void ReadFromStream_Ver_2(Stream input_stream, Encoding encoding)
+ {
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2");
+ Byte[] input_buffer = new Byte[10]; // Max read will be 4 + 2 + 4
- // Temporary FieldData object to get data from stream and put in array.
- NpgsqlRowDescriptionFieldData fd;
+ // Read the number of fields.
+ input_stream.Read(input_buffer, 0, 2);
+ Int16 num_fields = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 0));
- // Now, iterate through each field getting its data.
- for (Int16 i = 0; i < num_fields; i++)
- {
- fd = new NpgsqlRowDescriptionFieldData();
- // Set field name.
- fd.name = PGUtil.ReadString(input_stream, encoding);
+ // Temporary FieldData object to get data from stream and put in array.
+ NpgsqlRowDescriptionFieldData fd;
- // Read type_oid(Int32), type_size(Int16), type_modifier(Int32)
- input_stream.Read(input_buffer, 0, 4 + 2 + 4);
+ // Now, iterate through each field getting its data.
+ for (Int16 i = 0; i < num_fields; i++)
+ {
+ fd = new NpgsqlRowDescriptionFieldData();
- fd.type_oid = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
- fd.type_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 4));
- fd.type_modifier = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 6));
+ // Set field name.
+ fd.name = PGUtil.ReadString(input_stream, encoding);
- // Add field data to array.
- fields_data.Add(fd);
+ // Read type_oid(Int32), type_size(Int16), type_modifier(Int32)
+ input_stream.Read(input_buffer, 0, 4 + 2 + 4);
- fields_index.Add(fd.name);
- }
- }
- else
- {
- Byte[] input_buffer = new Byte[4]; // Max read will be 4 + 2 + 4 + 2 + 4 + 2
+ fd.type_oid = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0));
+ fd.type_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 4));
+ fd.type_modifier = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 6));
+
+ // Add field data to array.
+ fields_data.Add(fd);
- // Read the length of message.
- // [TODO] Any use for now?
- PGUtil.ReadInt32(input_stream, input_buffer);
- Int16 num_fields = PGUtil.ReadInt16(input_stream, input_buffer);
+ fields_index.Add(fd.name);
+ }
+ }
- // Temporary FieldData object to get data from stream and put in array.
- NpgsqlRowDescriptionFieldData fd;
+ public void ReadFromStream_Ver_3(Stream input_stream, Encoding encoding)
+ {
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3");
- for (Int16 i = 0; i < num_fields; i++)
- {
- fd = new NpgsqlRowDescriptionFieldData();
-
- fd.name = PGUtil.ReadString(input_stream, encoding);
- fd.table_oid = PGUtil.ReadInt32(input_stream, input_buffer);
- fd.column_attribute_number = PGUtil.ReadInt16(input_stream, input_buffer);
- fd.type_oid = PGUtil.ReadInt32(input_stream, input_buffer);
- fd.type_size = PGUtil.ReadInt16(input_stream, input_buffer);
- fd.type_modifier = PGUtil.ReadInt32(input_stream, input_buffer);
- fd.format_code = (FormatCode)PGUtil.ReadInt16(input_stream, input_buffer);
-
- fields_data.Add(fd);
- fields_index.Add(fd.name);
- }
+ Byte[] input_buffer = new Byte[4]; // Max read will be 4 + 2 + 4 + 2 + 4 + 2
- }
+ // Read the length of message.
+ // [TODO] Any use for now?
+ PGUtil.ReadInt32(input_stream, input_buffer);
+ Int16 num_fields = PGUtil.ReadInt16(input_stream, input_buffer);
+ // Temporary FieldData object to get data from stream and put in array.
+ NpgsqlRowDescriptionFieldData fd;
+ for (Int16 i = 0; i < num_fields; i++)
+ {
+ fd = new NpgsqlRowDescriptionFieldData();
+
+ fd.name = PGUtil.ReadString(input_stream, encoding);
+ fd.table_oid = PGUtil.ReadInt32(input_stream, input_buffer);
+ fd.column_attribute_number = PGUtil.ReadInt16(input_stream, input_buffer);
+ fd.type_oid = PGUtil.ReadInt32(input_stream, input_buffer);
+ fd.type_size = PGUtil.ReadInt16(input_stream, input_buffer);
+ fd.type_modifier = PGUtil.ReadInt32(input_stream, input_buffer);
+ fd.format_code = (FormatCode)PGUtil.ReadInt16(input_stream, input_buffer);
+
+ fields_data.Add(fd);
+ fields_index.Add(fd.name);
+ }
}
public NpgsqlRowDescriptionFieldData this[Int32 index]
{
return (NpgsqlRowDescriptionFieldData)fields_data[index];
}
-
}
public Int16 NumFields
}
return -1;
-
}
}
///
internal sealed class NpgsqlStartupPacket
{
-
// Logging related values
private static readonly String CLASSNAME = "NpgsqlStartupPacket";
// Private fields.
private Int32 packet_size;
- private Int32 protocol_version;
+ private ProtocolVersion protocol_version;
private String database_name;
private String user_name;
private String arguments;
private String optional_tty;
public NpgsqlStartupPacket(Int32 packet_size,
- Int32 protocol_version_major,
- Int32 protocol_version_minor,
+ ProtocolVersion protocol_version,
String database_name,
String user_name,
String arguments,
// know what to send.
this.packet_size = packet_size;
- this.protocol_version = (protocol_version_major<<16) | protocol_version_minor;
+ this.protocol_version = protocol_version;
+
this.database_name = database_name;
this.user_name = user_name;
this.arguments = arguments;
public void WriteToStream(Stream output_stream, Encoding encoding)
{
-
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream");
- // [FIXME] Need exception handling ?
-
- if (protocol_version == ProtocolVersion.Version2) // Protocol 2.0
- {
-
- // Packet length = 296
- output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(this.packet_size)), 0, 4);
-
- // Protocol version = 2.0
- output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(this.protocol_version)), 0, 4);
+ switch (protocol_version) {
+ case ProtocolVersion.Version2 :
+ WriteToStream_Ver_2(output_stream, encoding);
+ break;
- // Database name.
- PGUtil.WriteLimString(this.database_name, 64, output_stream, encoding);
+ case ProtocolVersion.Version3 :
+ WriteToStream_Ver_3(output_stream, encoding);
+ break;
- // User name.
- PGUtil.WriteLimString(this.user_name, 32, output_stream, encoding);
+ }
+ }
- // Arguments.
- PGUtil.WriteLimString(this.arguments, 64, output_stream, encoding);
- // Unused.
- PGUtil.WriteLimString(this.unused, 64, output_stream, encoding);
+ private void WriteToStream_Ver_2(Stream output_stream, Encoding encoding)
+ {
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream_Ver_2");
- // Optional tty.
- PGUtil.WriteLimString(this.optional_tty, 64, output_stream, encoding);
- output_stream.Flush();
+ // Packet length = 296
+ output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(this.packet_size)), 0, 4);
- }
+ output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(PGUtil.ConvertProtocolVersion(this.protocol_version))), 0, 4);
- if (protocol_version >= ProtocolVersion.Version3) // Protocol 3+
- {
- PGUtil.WriteInt32(output_stream, 4 + 4 + 5 + (encoding.GetByteCount(user_name) + 1) + 9 + (encoding.GetByteCount(database_name) + 1) + 10 + 4 + 1);
+ // Database name.
+ PGUtil.WriteLimString(this.database_name, 64, output_stream, encoding);
- // Protocol version = 3.0
+ // User name.
+ PGUtil.WriteLimString(this.user_name, 32, output_stream, encoding);
- PGUtil.WriteInt32(output_stream, this.protocol_version);
+ // Arguments.
+ PGUtil.WriteLimString(this.arguments, 64, output_stream, encoding);
- // User name.
+ // Unused.
+ PGUtil.WriteLimString(this.unused, 64, output_stream, encoding);
- PGUtil.WriteString("user", output_stream, encoding);
+ // Optional tty.
+ PGUtil.WriteLimString(this.optional_tty, 64, output_stream, encoding);
+ output_stream.Flush();
+ }
- // User name.
- PGUtil.WriteString(user_name, output_stream, encoding);
- // Database name.
- PGUtil.WriteString("database", output_stream, encoding);
+ private void WriteToStream_Ver_3(Stream output_stream, Encoding encoding)
+ {
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream_Ver_3");
- // Database name.
- PGUtil.WriteString(database_name, output_stream, encoding);
+ PGUtil.WriteInt32(output_stream, 4 + 4 + 5 + (encoding.GetByteCount(user_name) + 1) + 9 + (encoding.GetByteCount(database_name) + 1) + 10 + 4 + 1);
- // DateStyle.
- PGUtil.WriteString("DateStyle", output_stream, encoding);
+ PGUtil.WriteInt32(output_stream, Npgsql.PGUtil.ConvertProtocolVersion(this.protocol_version));
- // DateStyle.
- PGUtil.WriteString("ISO", output_stream, encoding);
+ // User name.
+ PGUtil.WriteString("user", output_stream, encoding);
- output_stream.WriteByte(0);
- output_stream.Flush();
+ // User name.
+ PGUtil.WriteString(user_name, output_stream, encoding);
- }
+ // Database name.
+ PGUtil.WriteString("database", output_stream, encoding);
+ // Database name.
+ PGUtil.WriteString(database_name, output_stream, encoding);
+ // DateStyle.
+ PGUtil.WriteString("DateStyle", output_stream, encoding);
+ // DateStyle.
+ PGUtil.WriteString("ISO", output_stream, encoding);
+ output_stream.WriteByte(0);
+ output_stream.Flush();
}
}
}
protected virtual void ProcessBackendResponses( NpgsqlConnection context )
{
- switch (context.BackendProtocolVersion)
- {
+ switch (context.BackendProtocolVersion) {
case ProtocolVersion.Version2 :
ProcessBackendResponses_Ver_2(context);
break;
// Check the first Byte of response.
switch ( stream.ReadByte() )
{
- case NpgsqlMessageTypes.ErrorResponse :
+ case NpgsqlMessageTypes_Ver_2.ErrorResponse :
{
NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
break;
- case NpgsqlMessageTypes.AuthenticationRequest :
+ case NpgsqlMessageTypes_Ver_2.AuthenticationRequest :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
authType = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(inputBuffer, 0));
- if ( authType == NpgsqlMessageTypes.AuthenticationOk )
+ if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk )
{
NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
break;
}
- if ( authType == NpgsqlMessageTypes.AuthenticationClearTextPassword )
+ if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword )
{
NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
}
- if ( authType == NpgsqlMessageTypes.AuthenticationMD5Password )
+ if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password )
{
NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
// Now do the "MD5-Thing"
mediator.Errors.Add(String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType));
return;
- case NpgsqlMessageTypes.RowDescription:
+ case NpgsqlMessageTypes_Ver_2.RowDescription:
// This is the RowDescription message.
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
// Now wait for the AsciiRow messages.
break;
- case NpgsqlMessageTypes.AsciiRow:
+ case NpgsqlMessageTypes_Ver_2.AsciiRow:
// This is the AsciiRow message.
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");
// Now wait for CompletedResponse message.
break;
- case NpgsqlMessageTypes.BinaryRow:
+ case NpgsqlMessageTypes_Ver_2.BinaryRow:
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow");
NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(rd);
break;
- case NpgsqlMessageTypes.ReadyForQuery :
+ case NpgsqlMessageTypes_Ver_2.ReadyForQuery :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
readyForQuery = true;
ChangeState( context, NpgsqlReadyState.Instance );
break;
- case NpgsqlMessageTypes.BackendKeyData :
+ case NpgsqlMessageTypes_Ver_2.BackendKeyData :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
// BackendKeyData message.
NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
backend_keydata.ReadFromStream(stream);
- mediator.AddBackendKeydata(backend_keydata);
+ mediator.SetBackendKeydata(backend_keydata);
// Wait for ReadForQuery message
break;
;
- case NpgsqlMessageTypes.NoticeResponse :
+ case NpgsqlMessageTypes_Ver_2.NoticeResponse :
{
NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
// Wait for ReadForQuery message
break;
- case NpgsqlMessageTypes.CompletedResponse :
+ case NpgsqlMessageTypes_Ver_2.CompletedResponse :
// This is the CompletedResponse message.
// Get the string returned.
// Now wait for ReadyForQuery message.
break;
- case NpgsqlMessageTypes.CursorResponse :
+ case NpgsqlMessageTypes_Ver_2.CursorResponse :
// This is the cursor response message.
// It is followed by a C NULL terminated string with the name of
// the cursor in a FETCH case or 'blank' otherwise.
// Continue waiting for ReadyForQuery message.
break;
- case NpgsqlMessageTypes.ParseComplete :
- NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
- // Just read up the message length.
- PGUtil.ReadInt32(stream, new Byte[4]);
- readyForQuery = true;
- break;
-
- case NpgsqlMessageTypes.BindComplete :
- NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
- // Just read up the message length.
- PGUtil.ReadInt32(stream, new Byte[4]);
- readyForQuery = true;
- break;
-
- case NpgsqlMessageTypes.EmptyQueryResponse :
+ case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
// This is the EmptyQueryResponse.
// [FIXME] Just ignore it this way?
PGUtil.ReadString(stream, context.Encoding);
break;
- case NpgsqlMessageTypes.NotificationResponse :
+ case NpgsqlMessageTypes_Ver_2.NotificationResponse :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");
// Wait for ReadForQuery message
break;
- case NpgsqlMessageTypes.ParameterStatus :
- NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
- NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus();
- parameterStatus.ReadFromStream(stream, context.Encoding);
+ default :
+ // This could mean a number of things
+ // We've gotten out of sync with the backend?
+ // We need to implement this type?
+ // Backend has gone insane?
+ // FIXME
+ // what exception should we really throw here?
+ throw new NotSupportedException("Backend sent unrecognized response type");
- NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue);
- if (parameterStatus.Parameter == "server_version")
- context.ServerVersion = parameterStatus.ParameterValue;
- break;
}
}
-
}
protected virtual void ProcessBackendResponses_Ver_3( NpgsqlConnection context )
// Check the first Byte of response.
switch ( stream.ReadByte() )
{
- case NpgsqlMessageTypes.ErrorResponse :
+ case NpgsqlMessageTypes_Ver_3.ErrorResponse :
{
NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
break;
- case NpgsqlMessageTypes.AuthenticationRequest :
+ case NpgsqlMessageTypes_Ver_3.AuthenticationRequest :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
authType = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(inputBuffer, 0));
- if ( authType == NpgsqlMessageTypes.AuthenticationOk )
+ if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk )
{
NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
break;
}
- if ( authType == NpgsqlMessageTypes.AuthenticationClearTextPassword )
+ if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword )
{
NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
}
- if ( authType == NpgsqlMessageTypes.AuthenticationMD5Password )
+ if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password )
{
NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
// Now do the "MD5-Thing"
mediator.Errors.Add(String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType));
return;
- case NpgsqlMessageTypes.RowDescription:
+ case NpgsqlMessageTypes_Ver_3.RowDescription:
// This is the RowDescription message.
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
// Now wait for the AsciiRow messages.
break;
- case NpgsqlMessageTypes.AsciiRow:
+ case NpgsqlMessageTypes_Ver_3.DataRow:
// This is the AsciiRow message.
- NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");
+ NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(rd, context.OidToNameMapping, context.BackendProtocolVersion);
asciiRow.ReadFromStream(stream, context.Encoding);
// Now wait for CompletedResponse message.
break;
- case NpgsqlMessageTypes.ReadyForQuery :
+ case NpgsqlMessageTypes_Ver_3.ReadyForQuery :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
break;
- case NpgsqlMessageTypes.BackendKeyData :
+ case NpgsqlMessageTypes_Ver_3.BackendKeyData :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
// BackendKeyData message.
NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
backend_keydata.ReadFromStream(stream);
- mediator.AddBackendKeydata(backend_keydata);
+ mediator.SetBackendKeydata(backend_keydata);
// Wait for ReadForQuery message
break;
- case NpgsqlMessageTypes.NoticeResponse :
+ case NpgsqlMessageTypes_Ver_3.NoticeResponse :
// Notices and errors are identical except that we
// just throw notices away completely ignored.
// Wait for ReadForQuery message
break;
- case NpgsqlMessageTypes.CompletedResponse :
+ case NpgsqlMessageTypes_Ver_3.CompletedResponse :
// This is the CompletedResponse message.
// Get the string returned.
break;
- case NpgsqlMessageTypes.CursorResponse :
- // This is the cursor response message.
- // It is followed by a C NULL terminated string with the name of
- // the cursor in a FETCH case or 'blank' otherwise.
- // In this case it should be always 'blank'.
- // [FIXME] Get another name for this function.
- NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");
-
- PGUtil.ReadString(stream, context.Encoding);
- // Continue waiting for ReadyForQuery message.
- break;
-
- case NpgsqlMessageTypes.ParseComplete :
+ case NpgsqlMessageTypes_Ver_3.ParseComplete :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
// Just read up the message length.
PGUtil.ReadInt32(stream, Buff);
readyForQuery = true;
break;
- case NpgsqlMessageTypes.BindComplete :
+ case NpgsqlMessageTypes_Ver_3.BindComplete :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
// Just read up the message length.
PGUtil.ReadInt32(stream, Buff);
readyForQuery = true;
break;
- case NpgsqlMessageTypes.EmptyQueryResponse :
+ case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
// This is the EmptyQueryResponse.
// [FIXME] Just ignore it this way?
PGUtil.ReadInt32(stream, Buff);
break;
- case NpgsqlMessageTypes.NotificationResponse :
-
+ case NpgsqlMessageTypes_Ver_3.NotificationResponse :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");
// Eat the length
// Wait for ReadForQuery message
break;
- case NpgsqlMessageTypes.ParameterStatus :
+ case NpgsqlMessageTypes_Ver_3.ParameterStatus :
NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus();
parameterStatus.ReadFromStream(stream, context.Encoding);
NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue);
- if (parameterStatus.Parameter == "server_version")
- context.ServerVersion = parameterStatus.ParameterValue;
+
+ mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus);
+
+ if (parameterStatus.Parameter == "server_version") {
+ // Add this one under our own name so that if the parameter name
+ // changes in a future backend version, we can handle it here in the
+ // protocol handler and leave everybody else put of it.
+ mediator.AddParameterStatus("__npgsql_server_version", parameterStatus);
+// context.ServerVersionString = parameterStatus.ParameterValue;
+ }
+
break;
+
+ default :
+ // This could mean a number of things
+ // We've gotten out of sync with the backend?
+ // We need to implement this type?
+ // Backend has gone insane?
+ // FIXME
+ // what exception should we really throw here?
+ throw new NotSupportedException("Backend sent unrecognized response type");
+
}
}
}
namespace Npgsql
{
+ /// <summary>
+ /// Represent the frontend/backend protocol version in use.
+ /// </summary>
+ internal enum ProtocolVersion
+ {
+ Version2,
+ Version3
+ }
- internal struct ProtocolVersion
+ /// <summary>
+ /// Represent the backend server version.
+ /// </summary>
+ internal class ServerVersion
{
- public const Int32 Version2 = 131072;
- public const Int32 Version3 = 196608;
+ public static readonly Int32 ProtocolVersion2 = 2 << 16; // 131072
+ public static readonly Int32 ProtocolVersion3 = 3 << 16; // 196608
+
+ public String Raw;
+ public Int32 Major;
+ public Int32 Minor;
+ public Int32 Patch;
+
+ private ServerVersion()
+ {}
+
+ public ServerVersion(Int32 Major, Int32 Minor, Int32 Patch)
+ {
+ this.Raw = string.Format("{0}.{1}.{2}", Major, Minor, Patch);
+ this.Major = Major;
+ this.Minor = Minor;
+ this.Patch = Patch;
+ }
+
+ public bool GreaterOrEqual(Int32 Major, Int32 Minor, Int32 Patch)
+ {
+ return
+ (this.Major > Major) ||
+ (this.Major == Major && this.Minor > Minor) ||
+ (this.Major == Major && this.Minor == Minor && this.Patch >= Patch);
+ }
+
+ public new String ToString()
+ {
+ return Raw;
+ }
}
-internal enum FormatCode:
+ internal enum FormatCode:
short
{
Text = 0,
Binary = 1
}
-
///<summary>
/// This class provides many util methods to handle
/// reading and writing of PostgreSQL protocol messages.
/// Should it be abstract or with a private constructor to prevent
/// creating instances?
- //
internal sealed class PGUtil
{
private static readonly String CLASSNAME = "PGUtil";
private static ResourceManager resman = new ResourceManager(typeof(PGUtil));
+ ///<summary>
+ /// This method takes a ProtocolVersion and returns an integer
+ /// version number that the Postgres backend will recognize in a
+ /// startup packet.
+ /// </summary>
+
+ public static Int32 ConvertProtocolVersion(ProtocolVersion Ver)
+ {
+ switch (Ver) {
+ case ProtocolVersion.Version2 :
+ return ServerVersion.ProtocolVersion2;
+
+ case ProtocolVersion.Version3 :
+ return ServerVersion.ProtocolVersion3;
+
+ }
+
+ // CHECKME
+ // should we throw?
+ return 0;
+ }
+
///<summary>
/// This method gets a C NULL terminated string from the network stream.
/// It keeps reading a byte in each time until a NULL byte is returned.