internal void Open()
{
ServerVersion = null;
// If Connection.ConnectionString specifies a protocol version, we will
// not try to fall back to version 2 on failure.
_backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown)
? ProtocolVersion.Version3
: settings.Protocol;
// Reset state to initialize new connector in pool.
CurrentState = NpgsqlClosedState.Instance;
// Keep track of time remaining; Even though there may be multiple timeout-able calls,
// this allows us to still respect the caller's timeout expectation.
int connectTimeRemaining = this.ConnectionTimeout * 1000;
DateTime attemptStart = DateTime.Now;
// Get a raw connection, possibly SSL...
CurrentState.Open(this, connectTimeRemaining);
try
{
// Establish protocol communication and handle authentication...
CurrentState.Startup(this,settings);
}
catch (NpgsqlException ne)
{
if (_stream != null)
{
try
{
_stream.Dispose();
}
catch
{
}
}
connectTimeRemaining -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds);
// Check for protocol not supported. If we have been told what protocol to use,
// we will not try this step.
if (settings.Protocol != ProtocolVersion.Unknown)
{
throw;
}
// If we attempted protocol version 3, it may be possible to drop back to version 2.
if (BackendProtocolVersion != ProtocolVersion.Version3)
{
throw;
}
NpgsqlError Error0 = (NpgsqlError) ne.Errors[0];
// If NpgsqlError..ctor() encounters a version 2 error,
// it will set its own protocol version to version 2. That way, we can tell
// easily if the error was a FATAL: protocol error.
if (Error0.BackendProtocolVersion != ProtocolVersion.Version2)
{
throw;
}
// Try using the 2.0 protocol.
BackendProtocolVersion = ProtocolVersion.Version2;
CurrentState = NpgsqlClosedState.Instance;
// Get a raw connection, possibly SSL...
CurrentState.Open(this, connectTimeRemaining);
// Establish protocol communication and handle authentication...
CurrentState.Startup(this,this.settings);
}
// Change the state of connection to open and ready.
_connection_state = ConnectionState.Open;
CurrentState = NpgsqlReadyState.Instance;
// After attachment, the stream will close the connector (this) when the stream gets disposed.
_baseStream.AttachConnector(this);
// Fall back to the old way, SELECT VERSION().
// This should not happen for protocol version 3+.
if (ServerVersion == null)
{
//NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this);
//ServerVersion = new Version(PGUtil.ExtractServerVersion((string) command.ExecuteScalar()));
using (NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this))
{
ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar()));
}
}
ProcessServerVersion();
StringWriter sbInitQueries = new StringWriter();
if (BackendProtocolVersion == ProtocolVersion.Version2)
{
sbInitQueries.WriteLine("SET DATESTYLE TO ISO;");
// Adjust client encoding.
NpgsqlParameterStatus clientEncodingParam = null;
if (
!ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) ||
(!string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase))
)
{
sbInitQueries.WriteLine("SET CLIENT_ENCODING TO UTF8;");
}
if (!string.IsNullOrEmpty(settings.SearchPath))
{
// TODO: Add proper message when finding a semicolon in search_path.
// This semicolon could lead to a sql injection security hole as someone could write in connection string:
// searchpath=public;delete from table; and it would be executed.
if (settings.SearchPath.Contains(";"))
{
throw new InvalidOperationException();
}
// This is using string concatenation because set search_path doesn't allow type casting. ::text
sbInitQueries.WriteLine("SET SEARCH_PATH = {0};", settings.SearchPath);
}
if (!string.IsNullOrEmpty(settings.ApplicationName))
{
if (!SupportsApplicationName)
{
//TODO
//throw new InvalidOperationException(resman.GetString("Exception_ApplicationNameNotSupported"));
throw new InvalidOperationException("ApplicationName not supported.");
}
if (settings.ApplicationName.Contains(";"))
{
throw new InvalidOperationException();
}
sbInitQueries.WriteLine("SET APPLICATION_NAME='{0}';", settings.ApplicationName);
}
/*
* Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made
* postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it.
* Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here.
* This only works on postgresql servers where the ssl renegotiation settings is supported of course.
* See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information.
*/
if (SupportsSslRenegotiationLimit)
{
sbInitQueries.WriteLine("SET ssl_renegotiation_limit=0;");
}
/*
* Set precision digits to maximum value possible. For postgresql before 9 it was 2, after that, it is 3.
* Check bug report #1010992 for more information.
*/
if (SupportsExtraFloatDigits3)
{
sbInitQueries.WriteLine("SET extra_float_digits=3;");
}
else if (SupportsExtraFloatDigits)
{
sbInitQueries.WriteLine("SET extra_float_digits=2;");
}
/*
* Set lc_monetary format to 'C' in order to get a culture agnostic representation of money.
* I noticed that on Windows, even when the lc_monetary is English_United States.UTF-8, negative
* money is formatted as ($value) with parentheses to indicate negative value.
* By going with a culture agnostic format, we get a consistent behavior.
*/
sbInitQueries.WriteLine("SET lc_monetary='C';");
}
else
{
// Some connection parameters for protocol 3 had been sent in the startup packet.
// The rest will be setted here.
if (SupportsExtraFloatDigits3)
{
sbInitQueries.WriteLine("SET extra_float_digits=3;");
}
if (SupportsSslRenegotiationLimit)
{
sbInitQueries.WriteLine("SET ssl_renegotiation_limit=0;");
}
}
initQueries = sbInitQueries.ToString();
NpgsqlCommand.ExecuteBlind(this, initQueries, 60);
// Make a shallow copy of the type mapping that the connector will own.
// It is possible that the connector may add types to its private
// mapping that will not be valid to another connector, even
// if connected to the same backend version.
NativeToBackendTypeConverterOptions.OidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone();
// The connector is now fully initialized. Beyond this point, it is
// safe to release it back to the pool rather than closing it.
IsInitialized = true;
}