/// <summary> /// Initializes a new instance of the BaseSession class based on the specified connection string. /// </summary> /// <param name="connectionString">The connection used to create the session.</param> /// <exception cref="ArgumentNullException"><paramref name="connectionString"/> is <c>null</c>.</exception> /// <exception cref="UriFormatException">Unable to parse the <paramref name="connectionString"/> when /// in URI format.</exception> /// <remarks> /// <para>When using Unix sockets the <c>protocol=unix</c> or <c>protocol=unixsocket</c> connection option is required. /// This will enable elements passed in the <c>server</c> connection option to be treated as Unix sockets. The user is also required /// to explicitly set <c>sslmode</c> to <c>none</c> since X Plugin does not support SSL when using Unix sockets. Note that /// <c>protocol=unix</c> and <c>protocol=unixsocket</c> are synonyms.</para> /// <para> </para> /// <para>Multiple hosts can be specified as part of the <paramref name="connectionString"/>, /// which will enable client side failover when trying to establish a connection.</para> /// <para> </para> /// <para>Connection string examples (in URI format): /// <para />- mysqlx://test:test@[192.1.10.10,localhost] /// <para />- mysqlx://test:test@[192.1.10.10,127.0.0.1] /// <para />- mysqlx://root:@[../tmp/mysqlx.sock,/tmp/mysqld.sock]?protocol=unix&sslmode=none /// <para />- mysqlx://test:test@[192.1.10.10:33060,127.0.0.1:33060] /// <para />- mysqlx://test:test@[192.1.10.10,120.0.0.2:22000,[::1]:33060]/test?connectiontimeout=10 /// <para />- mysqlx://test:test@[(address=server.example,priority=20),(address=127.0.0.1,priority=100)] /// <para />- mysqlx://test:test@[(address=server.example,priority=100),(address=127.0.0.1,priority=75),(address=192.0.10.56,priority=25)] /// </para> /// <para> </para> /// <para>Connection string examples (in basic format): /// <para />- server=10.10.10.10,localhost;port=33060;uid=test;password=test; /// <para />- host=10.10.10.10,192.101.10.2,localhost;port=5202;uid=test;password=test; /// <para />- host=./tmp/mysqld.sock,/var/run/mysqldx.sock;port=5202;uid=root;protocol=unix;sslmode=none; /// <para />- server=(address=server.example,priority=20),(address=127.0.0.1,priority=100);port=33060;uid=test;password=test; /// <para />- server=(address=server.example,priority=100),(address=127.0.0.1,priority=75),(address=192.0.10.56,priority=25);port=33060;uid=test;password=test; /// </para> /// <para> </para> /// <para>Failover methods</para> /// <para>- Sequential: Connection attempts will be performed in a sequential order, that is, one after another until /// a connection is successful or all the elements from the list have been tried. /// </para> /// <para>- Priority based: If a priority is provided, the connection attemps will be performed in descending order, starting /// with the host with the highest priority. Priority must be a value between 0 and 100. Additionally, it is required to either /// give a priority for every host or no priority to any host. /// </para> /// </remarks> public BaseSession(string connectionString) { if (string.IsNullOrWhiteSpace(connectionString)) { throw new ArgumentNullException("connectionString"); } this.connectionString = ParseConnectionString(connectionString); if (FailoverManager.FailoverGroup != null) { // Multiple hosts were specified. _internalSession = FailoverManager.AttemptConnection(this.connectionString, out this.connectionString); Settings = new MySqlConnectionStringBuilder(this.connectionString); } else { // A single host was specified. Settings = new MySqlConnectionStringBuilder(this.connectionString); _internalSession = InternalSession.GetSession(Settings); } if (!string.IsNullOrWhiteSpace(Settings.Database)) { GetSchema(Settings.Database); } }
/// <summary> /// Initializes a new instance of the BaseSession class based on the specified connection string. /// </summary> /// <param name="connectionString">The connection used to create the session.</param> /// <exception cref="ArgumentNullException"><paramref name="connectionString"/> is <c>null</c>.</exception> /// <exception cref="UriFormatException">Unable to parse the <paramref name="connectionString"/> when /// in URI format.</exception> /// <remarks> /// <para>When using Unix sockets the <c>protocol=unix</c> or <c>protocol=unixsocket</c> connection option is required. /// This will enable elements passed in the <c>server</c> connection option to be treated as Unix sockets. The user is also required /// to explicitly set <c>sslmode</c> to <c>none</c> since X Plugin does not support SSL when using Unix sockets. Note that /// <c>protocol=unix</c> and <c>protocol=unixsocket</c> are synonyms.</para> /// <para> </para> /// <para>Multiple hosts can be specified as part of the <paramref name="connectionString"/>, /// which enables client-side failover when trying to establish a connection.</para> /// <para> </para> /// <para>Connection URI examples: /// <para />- mysqlx://test:test@[192.1.10.10,localhost] /// <para />- mysqlx://test:test@[192.1.10.10,127.0.0.1] /// <para />- mysqlx://root:@[../tmp/mysqlx.sock,/tmp/mysqld.sock]?protocol=unix&sslmode=none /// <para />- mysqlx://test:test@[192.1.10.10:33060,127.0.0.1:33060] /// <para />- mysqlx://test:test@[192.1.10.10,120.0.0.2:22000,[::1]:33060]/test?connectiontimeout=10 /// <para />- mysqlx://test:test@[(address=server.example,priority=20),(address=127.0.0.1,priority=100)] /// <para />- mysqlx://test:test@[(address=server.example,priority=100),(address=127.0.0.1,priority=75),(address=192.0.10.56,priority=25)] /// </para> /// <para> </para> /// <para>Connection string examples: /// <para />- server=10.10.10.10,localhost;port=33060;uid=test;password=test; /// <para />- host=10.10.10.10,192.101.10.2,localhost;port=5202;uid=test;password=test; /// <para />- host=./tmp/mysqld.sock,/var/run/mysqldx.sock;port=5202;uid=root;protocol=unix;sslmode=none; /// <para />- server=(address=server.example,priority=20),(address=127.0.0.1,priority=100);port=33060;uid=test;password=test; /// <para />- server=(address=server.example,priority=100),(address=127.0.0.1,priority=75),(address=192.0.10.56,priority=25);port=33060;uid=test;password=test; /// </para> /// <para> </para> /// <para>Failover methods</para> /// <para>- Sequential: Connection attempts will be performed in a sequential order, that is, one after another until /// a connection is successful or all the elements from the list have been tried. /// </para> /// <para>- Priority based: If a priority is provided, the connection attemps will be performed in descending order, starting /// with the host with the highest priority. Priority must be a value between 0 and 100. Additionally, it is required to either /// give a priority for every host or no priority to any host. /// </para> /// </remarks> internal BaseSession(string connectionString, Client client = null) : this() { if (string.IsNullOrWhiteSpace(connectionString)) { throw new ArgumentNullException("connectionString"); } _client = client; this._connectionString = ParseConnectionData(connectionString); // Multiple hosts were specified. if (FailoverManager.FailoverGroup != null) { _internalSession = FailoverManager.AttemptConnection(this._connectionString, out this._connectionString); Settings.ConnectionString = this._connectionString; Settings.AnalyzeConnectionString(this._connectionString, true); } // A single host was specified. else { Settings.ConnectionString = _connectionString; if (!(_connectionString.Contains("sslmode") || _connectionString.Contains("ssl mode") || _connectionString.Contains("ssl-mode"))) { Settings.SslMode = MySqlSslMode.Required; } Settings.AnalyzeConnectionString(this._connectionString, true); _internalSession = InternalSession.GetSession(Settings); } // Set the default schema if provided by the user. if (!string.IsNullOrWhiteSpace(Settings.Database)) { DefaultSchema = GetSchema(Settings.Database); } }
/// <summary> /// Initializes a new instance of the BaseSession class based on the specified anonymous type object. /// </summary> /// <param name="connectionData">The connection data as an anonymous type used to create the session.</param> /// <exception cref="ArgumentNullException"><paramref name="connectionData"/> is null.</exception> /// <remarks> /// <para>Multiple hosts can be specified as part of the <paramref name="connectionData"/>, which will enable client-side failover when trying to /// establish a connection.</para> /// <para> </para> /// <para>To assign multiple hosts create a property similar to the connection string examples (in basic format) shown in /// <see cref="BaseSession(string)"/>. Note that the value of the property must be a string. /// </para> /// </remarks> public BaseSession(object connectionData) { if (connectionData == null) { throw new ArgumentNullException("connectionData"); } var values = Tools.GetDictionaryFromAnonymous(connectionData); if (!values.Keys.Any(s => s.ToLowerInvariant() == "port")) { values.Add("port", newDefaultPort); } Settings = new MySqlConnectionStringBuilder(); bool hostsParsed = false; foreach (var value in values) { if (!Settings.ContainsKey(value.Key)) { throw new KeyNotFoundException(string.Format(ResourcesX.InvalidConnectionStringAttribute, value.Key)); } Settings.SetValue(value.Key, value.Value); if (!hostsParsed && !string.IsNullOrEmpty(Settings["server"].ToString())) { var server = value.Value.ToString(); if (IsUnixSocket(server)) { Settings.SetValue(value.Key, server = NormalizeUnixSocket(server)); } ParseHostList(server, false); if (FailoverManager.FailoverGroup != null) { Settings["server"] = FailoverManager.FailoverGroup.ActiveHost.Host; } hostsParsed = true; } } this.connectionString = Settings.ToString(); if (FailoverManager.FailoverGroup != null) { // Multiple hosts were specified. _internalSession = FailoverManager.AttemptConnection(this.connectionString, out this.connectionString); Settings = new MySqlConnectionStringBuilder(this.connectionString); } else { _internalSession = InternalSession.GetSession(Settings); } if (!string.IsNullOrWhiteSpace(Settings.Database)) { GetSchema(Settings.Database); } }
/// <summary> /// Parses the connection data. /// </summary> /// <param name="connectionData">The connection string or connection URI.</param> /// <returns>An updated connection string representation of the provided connection string or connection URI.</returns> protected internal string ParseConnectionData(string connectionData) { FailoverManager.Reset(); if (Regex.IsMatch(connectionData, @"^mysqlx(\+\w+)?://.*", RegexOptions.IgnoreCase)) { return(ParseConnectionUri(connectionData)); } else { return(ParseConnectionString(connectionData)); } }
/// <summary> /// Parses the connection string. /// </summary> /// <param name="connectionString">The connection string in basic or URI format.</param> /// <returns>An updated connection string in basic format.</returns> /// <remarks>The format (basic or URI) of the connection string is determined as well as the /// prescence of multiple hosts.</remarks> protected internal string ParseConnectionString(string connectionString) { FailoverManager.Reset(); // Connection string is in URI format. if (Regex.IsMatch(connectionString, @"^mysqlx(\+\w+)?://.*", RegexOptions.IgnoreCase)) { return(ParseUriConnectionString(connectionString)); } else { return(ParseBasicConnectionString(connectionString)); } }
/// <summary> /// Initializes a new instance of the BaseSession class based on the specified connection string. /// </summary> /// <param name="connectionString">The connection used to create the session.</param> /// <param name="client">A <see cref="Client"/> object.</param> /// <exception cref="ArgumentNullException"><paramref name="connectionString"/> is <c>null</c>.</exception> /// <exception cref="UriFormatException">Unable to parse the <paramref name="connectionString"/> when /// in URI format.</exception> /// <remarks> /// <para>When using Unix sockets the <c>protocol=unix</c> or <c>protocol=unixsocket</c> connection option is required. /// This will enable elements passed in the <c>server</c> connection option to be treated as Unix sockets. The user is also required /// to explicitly set <c>sslmode</c> to <c>none</c> since X Plugin does not support SSL when using Unix sockets. Note that /// <c>protocol=unix</c> and <c>protocol=unixsocket</c> are synonyms.</para> /// <para> </para> /// <para>Multiple hosts can be specified as part of the <paramref name="connectionString"/>, /// which enables client-side failover when trying to establish a connection.</para> /// <para> </para> /// <para>Connection URI examples: /// <para />- mysqlx://test:test@[192.1.10.10,localhost] /// <para />- mysqlx://test:test@[192.1.10.10,127.0.0.1] /// <para />- mysqlx://root:@[../tmp/mysqlx.sock,/tmp/mysqld.sock]?protocol=unix&sslmode=none /// <para />- mysqlx://test:test@[192.1.10.10:33060,127.0.0.1:33060] /// <para />- mysqlx://test:test@[192.1.10.10,120.0.0.2:22000,[::1]:33060]/test?connectiontimeout=10 /// <para />- mysqlx://test:test@[(address=server.example,priority=20),(address=127.0.0.1,priority=100)] /// <para />- mysqlx://test:test@[(address=server.example,priority=100),(address=127.0.0.1,priority=75),(address=192.0.10.56,priority=25)] /// </para> /// <para> </para> /// <para>Connection string examples: /// <para />- server=10.10.10.10,localhost;port=33060;uid=test;password=test; /// <para />- host=10.10.10.10,192.101.10.2,localhost;port=5202;uid=test;password=test; /// <para />- host=./tmp/mysqld.sock,/var/run/mysqldx.sock;port=5202;uid=root;protocol=unix;sslmode=none; /// <para />- server=(address=server.example,priority=20),(address=127.0.0.1,priority=100);port=33060;uid=test;password=test; /// <para />- server=(address=server.example,priority=100),(address=127.0.0.1,priority=75),(address=192.0.10.56,priority=25);port=33060;uid=test;password=test; /// </para> /// <para> </para> /// <para>Failover methods</para> /// <para>- Sequential: Connection attempts will be performed in a sequential order, that is, one after another until /// a connection is successful or all the elements from the list have been tried. /// </para> /// <para>- Priority based: If a priority is provided, the connection attemps will be performed in descending order, starting /// with the host with the highest priority. Priority must be a value between 0 and 100. Additionally, it is required to either /// give a priority for every host or no priority to any host. /// </para> /// </remarks> internal BaseSession(string connectionString, Client client = null) : this() { if (string.IsNullOrWhiteSpace(connectionString)) { throw new ArgumentNullException("connectionString"); } _client = client; this._connectionString = ParseConnectionData(connectionString, client); // Multiple hosts were specified. if (FailoverManager.FailoverGroup != null && FailoverManager.FailoverGroup.Hosts?.Count > 1) { _internalSession = FailoverManager.AttemptConnectionXProtocol(this._connectionString, out this._connectionString, _isDefaultPort, client); Settings.ConnectionString = this._connectionString; Settings.AnalyzeConnectionString(this._connectionString, true, _isDefaultPort); } // A single host was specified. else { Settings.ConnectionString = _connectionString; if (!(_connectionString.Contains("sslmode") || _connectionString.Contains("ssl mode") || _connectionString.Contains("ssl-mode"))) { Settings.SslMode = MySqlSslMode.Required; } Settings.AnalyzeConnectionString(this._connectionString, true, _isDefaultPort); if (Settings.DnsSrv) { var dnsSrvRecords = DnsResolver.GetDnsSrvRecords(Settings.Server); FailoverManager.SetHostList(dnsSrvRecords.ConvertAll(r => new FailoverServer(r.Target, r.Port, r.Priority)), FailoverMethod.Sequential); _internalSession = FailoverManager.AttemptConnectionXProtocol(this._connectionString, out this._connectionString, _isDefaultPort, client); Settings.ConnectionString = this._connectionString; } else { _internalSession = InternalSession.GetSession(Settings); } } // Set the default schema if provided by the user. if (!string.IsNullOrWhiteSpace(Settings.Database)) { DefaultSchema = GetSchema(Settings.Database); } }
public void ReleaseConnection(Driver driver) { lock ((_inUsePool as ICollection).SyncRoot) { if (_inUsePool.Contains(driver)) { _inUsePool.Remove(driver); } } if (driver.ConnectionLifetimeExpired() || BeingCleared) { driver.Close(); Debug.Assert(!_idlePool.Contains(driver)); } else { lock ((_idlePool as ICollection).SyncRoot) { EnqueueIdle(driver); } } lock (_dnsSrvLock) { if (driver.Settings.DnsSrv) { var dnsSrvRecords = DnsResolver.GetDnsSrvRecords(DnsResolver.ServiceName); FailoverManager.SetHostList(dnsSrvRecords.ConvertAll(r => new FailoverServer(r.Target, r.Port, null)), FailoverMethod.Sequential); foreach (var idleConnection in _idlePool) { string idleServer = idleConnection.Settings.Server; if (!FailoverManager.FailoverGroup.Hosts.Exists(h => h.Host == idleServer) && !idleConnection.IsInActiveUse) { idleConnection.Close(); } } } } Interlocked.Increment(ref _available); _autoEvent.Set(); }
/// <include file='docs/MySqlConnection.xml' path='docs/Close/*'/> public override void Close() { if (driver != null) { driver.IsPasswordExpired = false; } if (State == ConnectionState.Closed) { return; } if (Reader != null) { Reader.Close(); } // if the reader was opened with CloseConnection then driver // will be null on the second time through if (driver != null) { //TODO: Add support for 452 and 46X if (driver.currentTransaction == null) { CloseFully(); } //TODO: Add support for 452 and 46X else { driver.IsInActiveUse = false; } } if (Settings.ConnectionProtocol == MySqlConnectionProtocol.Tcp && Settings.IsSshEnabled()) { _sshHandler?.StopClient(); } FailoverManager.Reset(); MySqlPoolManager.Hosts = null; SetState(ConnectionState.Closed, true); }
/// <summary> /// Initializes the <see cref="FailoverManager"/> if more than one host is found. /// </summary> /// <param name="hierPart">A string containing an unparsed list of hosts.</param> /// <param name="connectionDataIsUri"><c>true</c> if the connection data is a URI; otherwise <c>false</c>.</param> /// <returns>The number of hosts found, -1 if an error was raised during parsing.</returns> private int ParseHostList(string hierPart, bool connectionDataIsUri) { if (string.IsNullOrWhiteSpace(hierPart)) { return(-1); } int hostCount = -1; FailoverMethod failoverMethod = FailoverMethod.Sequential; string[] hostArray = null; List <XServer> hostList = new List <XServer>(); hierPart = hierPart.Replace(" ", ""); if (!hierPart.StartsWith("(") && !hierPart.EndsWith(")")) { hostArray = hierPart.Split(','); foreach (var host in hostArray) { if (IsUnixSocket(host)) { hostList.Add(new XServer(NormalizeUnixSocket(host), -1, -1)); } else { hostList.Add(this.ConvertToXServer(host, connectionDataIsUri)); } } if (hostArray.Length == 1) { return(1); } hostCount = hostArray.Length; } else { string[] groups = hierPart.Split(new string[] { "),(" }, StringSplitOptions.RemoveEmptyEntries); bool? allHavePriority = null; int defaultPriority = 100; foreach (var group in groups) { // Remove leading parenthesis. var normalizedGroup = group; if (normalizedGroup.StartsWith("(")) { normalizedGroup = group.Substring(1); } if (normalizedGroup.EndsWith(")")) { normalizedGroup = normalizedGroup.Substring(0, normalizedGroup.Length - 1); } string[] items = normalizedGroup.Split(','); string[] keyValuePairs = items[0].Split(CONNECTION_DATA_VALUE_SEPARATOR); if (keyValuePairs[0].ToLowerInvariant() != "address") { throw new KeyNotFoundException(string.Format(ResourcesX.KeywordNotFound, "address")); } string host = keyValuePairs[1]; if (string.IsNullOrWhiteSpace(host)) { throw new ArgumentNullException(SERVER_CONNECTION_OPTION_KEYWORD); } if (items.Length == 2) { if (allHavePriority != null && allHavePriority == false) { throw new ArgumentException(ResourcesX.PriorityForAllOrNoHosts); } allHavePriority = allHavePriority ?? true; keyValuePairs = items[1].Split('='); if (keyValuePairs[0].ToLowerInvariant() != "priority") { throw new KeyNotFoundException(string.Format(ResourcesX.KeywordNotFound, "priority")); } if (string.IsNullOrWhiteSpace(keyValuePairs[1])) { throw new ArgumentNullException("priority"); } int priority = -1; Int32.TryParse(keyValuePairs[1], out priority); if (priority < 0 || priority > 100) { throw new ArgumentException(ResourcesX.PriorityOutOfLimits); } hostList.Add(ConvertToXServer(IsUnixSocket(host) ? NormalizeUnixSocket(host) : host, connectionDataIsUri, priority)); } else { if (allHavePriority != null && allHavePriority == true) { throw new ArgumentException(ResourcesX.PriorityForAllOrNoHosts); } allHavePriority = allHavePriority ?? false; hostList.Add(ConvertToXServer(host, connectionDataIsUri, defaultPriority > 0 ? defaultPriority-- : 0)); } } hostCount = groups.Length; failoverMethod = FailoverMethod.Priority; } FailoverManager.SetHostList(hostList, failoverMethod); return(hostCount); }
/// <summary> /// Initializes a new instance of the BaseSession class based on the specified anonymous type object. /// </summary> /// <param name="connectionData">The connection data as an anonymous type used to create the session.</param> /// <exception cref="ArgumentNullException"><paramref name="connectionData"/> is null.</exception> /// <remarks> /// <para>Multiple hosts can be specified as part of the <paramref name="connectionData"/>, which enables client-side failover when trying to /// establish a connection.</para> /// <para> </para> /// <para>To assign multiple hosts, create a property similar to the connection string examples shown in /// <see cref="BaseSession(string)"/>. Note that the value of the property must be a string. /// </para> /// </remarks> internal BaseSession(object connectionData, Client client = null) : this() { if (connectionData == null) { throw new ArgumentNullException("connectionData"); } _client = client; var values = Tools.GetDictionaryFromAnonymous(connectionData); if (!values.Keys.Any(s => s.ToLowerInvariant() == PORT_CONNECTION_OPTION_KEYWORD)) { values.Add(PORT_CONNECTION_OPTION_KEYWORD, X_PROTOCOL_DEFAULT_PORT); } bool hostsParsed = false; foreach (var value in values) { if (!Settings.ContainsKey(value.Key)) { throw new KeyNotFoundException(string.Format(ResourcesX.InvalidConnectionStringAttribute, value.Key)); } Settings.SetValue(value.Key, value.Value); if (!hostsParsed && !string.IsNullOrEmpty(Settings[SERVER_CONNECTION_OPTION_KEYWORD].ToString())) { var server = value.Value.ToString(); if (IsUnixSocket(server)) { Settings.SetValue(value.Key, server = NormalizeUnixSocket(server)); } ParseHostList(server, false); if (FailoverManager.FailoverGroup != null) { Settings[SERVER_CONNECTION_OPTION_KEYWORD] = null; } hostsParsed = true; } } this._connectionString = Settings.ToString(); Settings.AnalyzeConnectionString(this._connectionString, true); if (FailoverManager.FailoverGroup != null) { // Multiple hosts were specified. _internalSession = FailoverManager.AttemptConnection(this._connectionString, out this._connectionString); Settings.ConnectionString = _connectionString; } else { _internalSession = InternalSession.GetSession(Settings); } if (!string.IsNullOrWhiteSpace(Settings.Database)) { DefaultSchema = GetSchema(Settings.Database); } }
/// <summary> /// Parses a connection string. /// </summary> /// <param name="connectionString">The connection string to parse.</param> /// <returns>The parsed connection string.</returns> private string ParseConnectionString(string connectionString) { var updatedConnectionString = string.Empty; bool portProvided = false; bool isDnsSrv = false; var connectionOptionsDictionary = connectionString.Split(CONNECTION_DATA_KEY_SEPARATOR) .Select(item => item.Split(new char[] { CONNECTION_DATA_VALUE_SEPARATOR }, 2)) .Where(item => item.Length == 2) .ToDictionary(item => item[0], item => item[1]); var serverOption = MySqlXConnectionStringBuilder.Options.Options.First(item => item.Keyword == SERVER_CONNECTION_OPTION_KEYWORD); var connecttimeoutOption = MySqlXConnectionStringBuilder.Options.Options.First(item => item.Keyword == CONNECT_TIMEOUT_CONNECTION_OPTION_KEYWORD); foreach (KeyValuePair <string, string> keyValuePair in connectionOptionsDictionary) { // Value is an equal or a semicolon if (keyValuePair.Value == "=" || keyValuePair.Value == "\"") { throw new MySqlException(string.Format(Resources.InvalidConnectionStringValue, (keyValuePair.Value == "\"" ? ";" : "="), keyValuePair.Key)); } // Key is not server or any of its synonyms. if (keyValuePair.Key != serverOption.Keyword && !serverOption.Synonyms.Contains(keyValuePair.Key)) { if ((connecttimeoutOption.Keyword == keyValuePair.Key || connecttimeoutOption.Synonyms.Contains(keyValuePair.Key)) && String.IsNullOrWhiteSpace(keyValuePair.Value)) { throw new FormatException(ResourcesX.InvalidConnectionTimeoutValue); } if (keyValuePair.Key == PORT_CONNECTION_OPTION_KEYWORD) { portProvided = true; } if (keyValuePair.Key == DNS_SRV_CONNECTION_OPTION_KEYWORD) { isDnsSrv = Convert.ToBoolean(keyValuePair.Value); } updatedConnectionString += $"{keyValuePair.Key}{CONNECTION_DATA_VALUE_SEPARATOR}{keyValuePair.Value}{CONNECTION_DATA_KEY_SEPARATOR}"; continue; } // Key is server or one of its synonyms. var updatedValue = keyValuePair.Value; if (IsUnixSocket(keyValuePair.Value)) { updatedValue = NormalizeUnixSocket(keyValuePair.Value); } // The value for the server connection option doesn't have a server list format. if (FailoverManager.ParseHostList(updatedValue, true, false) == 1 && FailoverManager.FailoverGroup == null) { updatedConnectionString = $"{SERVER_CONNECTION_OPTION_KEYWORD}{CONNECTION_DATA_VALUE_SEPARATOR}{updatedValue}{CONNECTION_DATA_KEY_SEPARATOR}{updatedConnectionString}"; } } // DNS SRV Validation - Port cannot be provided by the user and multihost is not allowed if dns-srv is true if (isDnsSrv) { if (portProvided) { throw new ArgumentException(Resources.DnsSrvInvalidConnOptionPort); } if (FailoverManager.FailoverGroup != null) { throw new ArgumentException(Resources.DnsSrvInvalidConnOptionMultihost); } } // Default port must be added if not provided by the user. if (FailoverManager.FailoverGroup == null) { return(portProvided ? updatedConnectionString : $"{updatedConnectionString}{CONNECTION_DATA_KEY_SEPARATOR}{PORT_CONNECTION_OPTION_KEYWORD}{CONNECTION_DATA_VALUE_SEPARATOR}{X_PROTOCOL_DEFAULT_PORT}"); } return($"{SERVER_CONNECTION_OPTION_KEYWORD}{CONNECTION_DATA_VALUE_SEPARATOR}{FailoverManager.FailoverGroup.ActiveHost.Host}{CONNECTION_DATA_KEY_SEPARATOR}" + (!portProvided ? $"{PORT_CONNECTION_OPTION_KEYWORD}{CONNECTION_DATA_VALUE_SEPARATOR}{X_PROTOCOL_DEFAULT_PORT}{CONNECTION_DATA_KEY_SEPARATOR}" : string.Empty) + updatedConnectionString); }
/// <summary> /// Parses a connection URI. /// </summary> /// <param name="connectionUri">The connection URI to parse.</param> /// <returns>The connection string representation of the provided <paramref name="connectionUri"/>.</returns> private string ParseConnectionUri(string connectionUri) { Uri uri = null; string updatedUri = null; bool parseServerAsUnixSocket = false; string hierPart = null; try { uri = new Uri(connectionUri); } catch (UriFormatException ex) { if (ex.Message != "Invalid URI: The hostname could not be parsed.") { throw ex; } // Identify if multiple hosts were specified. string[] splitUri = connectionUri.Split('@', '?'); if (splitUri.Length == 1) { throw ex; } hierPart = splitUri[1]; var schema = string.Empty; parseServerAsUnixSocket = IsUnixSocket(hierPart); bool isArray = hierPart.StartsWith("[") && hierPart.Contains("]"); // Remove schema. if ((!parseServerAsUnixSocket && hierPart.Contains("/")) && !isArray || (parseServerAsUnixSocket && hierPart.Contains(")/")) || (hierPart.StartsWith("[") && hierPart.Contains("]/") && isArray)) { schema = hierPart.Substring(hierPart.LastIndexOf('/') + 1); hierPart = hierPart.Substring(0, hierPart.Length - schema.Length - 1); } if (parseServerAsUnixSocket) { updatedUri = splitUri[0] + "@localhost" + (schema != string.Empty ? "/" + schema : string.Empty) + (splitUri.Length > 2 ? "?" + splitUri[2] : string.Empty); } else if (isArray) { hierPart = hierPart.Substring(1, hierPart.Length - 2); int hostCount = FailoverManager.ParseHostList(hierPart, true, true); if (FailoverManager.FailoverGroup != null) { hierPart = FailoverManager.FailoverGroup.ActiveHost.Host; parseServerAsUnixSocket = IsUnixSocket(FailoverManager.FailoverGroup.ActiveHost.Host); updatedUri = splitUri[0] + "@" + (parseServerAsUnixSocket ? "localhost" : hierPart) + (FailoverManager.FailoverGroup.ActiveHost.Port != -1 ? ":" + FailoverManager.FailoverGroup.ActiveHost.Port : string.Empty) + (schema != string.Empty ? "/" + schema : string.Empty) + (splitUri.Length == 3 ? "?" + splitUri[2] : string.Empty); } else if (hostCount == 1) { updatedUri = splitUri[0] + "@" + hierPart + (schema != string.Empty ? "/" + schema : string.Empty) + (splitUri.Length == 3 ? "?" + splitUri[2] : string.Empty); } else { throw ex; } } } if (uri == null) { uri = updatedUri == null ? new Uri(connectionUri) : new Uri(updatedUri); } if (uri.Scheme == DNS_SRV_URI_SCHEME) { if (FailoverManager.FailoverGroup != null && FailoverManager.FailoverGroup.Hosts?.Count > 1) { throw new ArgumentException(Resources.DnsSrvInvalidConnOptionMultihost); } if (!uri.IsDefaultPort) { throw new ArgumentException(Resources.DnsSrvInvalidConnOptionPort); } if (parseServerAsUnixSocket) { throw new ArgumentException(Resources.DnsSrvInvalidConnOptionUnixSocket); } } else if (uri.Scheme != MYSQLX_URI_SCHEME && uri.Scheme != SSH_URI_SCHEME) { throw new ArgumentException(string.Format(ResourcesX.DnsSrvInvalidScheme, uri.Scheme)); } return(ConvertToConnectionString(uri, hierPart, parseServerAsUnixSocket, uri.Scheme == DNS_SRV_URI_SCHEME)); }
/// <summary> /// Initializes a new instance of the BaseSession class based on the specified anonymous type object. /// </summary> /// <param name="connectionData">The connection data as an anonymous type used to create the session.</param> /// <param name="client">A <see cref="Client"/> object.</param> /// <exception cref="ArgumentNullException"><paramref name="connectionData"/> is null.</exception> /// <remarks> /// <para>Multiple hosts can be specified as part of the <paramref name="connectionData"/>, which enables client-side failover when trying to /// establish a connection.</para> /// <para> </para> /// <para>To assign multiple hosts, create a property similar to the connection string examples shown in /// <see cref="BaseSession(string)"/>. Note that the value of the property must be a string. /// </para> /// </remarks> internal BaseSession(object connectionData, Client client = null) : this() { if (connectionData == null) { throw new ArgumentNullException("connectionData"); } _client = client; if (client == null) { FailoverManager.Reset(); } var values = Tools.GetDictionaryFromAnonymous(connectionData); if (!values.Keys.Any(s => s.ToLowerInvariant() == PORT_CONNECTION_OPTION_KEYWORD)) { values.Add(PORT_CONNECTION_OPTION_KEYWORD, X_PROTOCOL_DEFAULT_PORT); } bool hostsParsed = false; foreach (var value in values) { if (!Settings.ContainsKey(value.Key)) { throw new KeyNotFoundException(string.Format(ResourcesX.InvalidConnectionStringAttribute, value.Key)); } Settings.SetValue(value.Key, value.Value); if (!hostsParsed && !string.IsNullOrEmpty(Settings[SERVER_CONNECTION_OPTION_KEYWORD].ToString())) { var server = value.Value.ToString(); if (IsUnixSocket(server)) { Settings.SetValue(value.Key, server = NormalizeUnixSocket(server)); } FailoverManager.ParseHostList(server, true, false); if (FailoverManager.FailoverGroup != null && FailoverManager.FailoverGroup.Hosts?.Count > 1) { Settings[SERVER_CONNECTION_OPTION_KEYWORD] = null; } else if (FailoverManager.FailoverGroup != null) { Settings[SERVER_CONNECTION_OPTION_KEYWORD] = FailoverManager.FailoverGroup.Hosts[0].Host; } hostsParsed = true; } } this._connectionString = Settings.ToString(); Settings.AnalyzeConnectionString(this._connectionString, true, _isDefaultPort); if (FailoverManager.FailoverGroup != null && FailoverManager.FailoverGroup.Hosts?.Count > 1) { // Multiple hosts were specified. _internalSession = FailoverManager.AttemptConnectionXProtocol(this._connectionString, out this._connectionString, _isDefaultPort, client); Settings.ConnectionString = _connectionString; } else { if (Settings.DnsSrv) { var dnsSrvRecords = DnsResolver.GetDnsSrvRecords(Settings.Server); FailoverManager.SetHostList(dnsSrvRecords.ConvertAll(r => new FailoverServer(r.Target, r.Port, null)), FailoverMethod.Sequential); _internalSession = FailoverManager.AttemptConnectionXProtocol(this._connectionString, out this._connectionString, _isDefaultPort, client); Settings.ConnectionString = this._connectionString; } else { _internalSession = InternalSession.GetSession(Settings); } } if (!string.IsNullOrWhiteSpace(Settings.Database)) { DefaultSchema = GetSchema(Settings.Database); } }
/// <include file='docs/MySqlConnection.xml' path='docs/Open/*'/> public override void Open() { if (State == ConnectionState.Open) { Throw(new InvalidOperationException(Resources.ConnectionAlreadyOpen)); } // start up our interceptors _exceptionInterceptor = new ExceptionInterceptor(this); commandInterceptor = new CommandInterceptor(this); SetState(ConnectionState.Connecting, true); AssertPermissions(); //TODO: SUPPORT FOR 452 AND 46X // if we are auto enlisting in a current transaction, then we will be // treating the connection as pooled if (Settings.AutoEnlist && Transaction.Current != null) { driver = DriverTransactionManager.GetDriverInTransaction(Transaction.Current); if (driver != null && (driver.IsInActiveUse || !driver.Settings.EquivalentTo(this.Settings))) { Throw(new NotSupportedException(Resources.MultipleConnectionsInTransactionNotSupported)); } } MySqlConnectionStringBuilder currentSettings = Settings; try { if (Settings.ConnectionProtocol == MySqlConnectionProtocol.Tcp && Settings.IsSshEnabled()) { _sshHandler = new Ssh( Settings.SshHostName, Settings.SshUserName, Settings.SshPassword, Settings.SshKeyFile, Settings.SshPassphrase, Settings.SshPort, Settings.Server, Settings.Port, false ); _sshHandler.StartClient(); } if (!Settings.Pooling || MySqlPoolManager.Hosts == null) { FailoverManager.Reset(); if (Settings.DnsSrv) { var dnsSrvRecords = DnsResolver.GetDnsSrvRecords(Settings.Server); FailoverManager.SetHostList(dnsSrvRecords.ConvertAll(r => new FailoverServer(r.Target, r.Port, null)), FailoverMethod.Sequential); } else { FailoverManager.ParseHostList(Settings.Server, false); } } // Load balancing && Failover if (ReplicationManager.IsReplicationGroup(Settings.Server)) { if (driver == null) { ReplicationManager.GetNewConnection(Settings.Server, false, this); } else { currentSettings = driver.Settings; } } else if (FailoverManager.FailoverGroup != null && !Settings.Pooling) { FailoverManager.AttemptConnection(this, Settings.ConnectionString, out string connectionString); currentSettings.ConnectionString = connectionString; } if (Settings.Pooling) { if (FailoverManager.FailoverGroup != null) { FailoverManager.AttemptConnection(this, Settings.ConnectionString, out string connectionString, true); currentSettings.ConnectionString = connectionString; } MySqlPool pool = MySqlPoolManager.GetPool(currentSettings); if (driver == null || !driver.IsOpen) { driver = pool.GetConnection(); } ProcedureCache = pool.ProcedureCache; } else { if (driver == null || !driver.IsOpen) { driver = Driver.Create(currentSettings); } ProcedureCache = new ProcedureCache((int)Settings.ProcedureCacheSize); } } catch (Exception) { SetState(ConnectionState.Closed, true); throw; } SetState(ConnectionState.Open, false); driver.Configure(this); if (driver.IsPasswordExpired && Settings.Pooling) { MySqlPoolManager.ClearPool(currentSettings); } if (!(driver.SupportsPasswordExpiration && driver.IsPasswordExpired)) { if (!string.IsNullOrEmpty(Settings.Database)) { ChangeDatabase(Settings.Database); } } // setup our schema provider _schemaProvider = new ISSchemaProvider(this); PerfMonitor = new PerformanceMonitor(this); // if we are opening up inside a current transaction, then autoenlist // TODO: control this with a connection string option if (Transaction.Current != null && Settings.AutoEnlist) { EnlistTransaction(Transaction.Current); } hasBeenOpen = true; SetState(ConnectionState.Open, true); }