예제 #1
0
        /// <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>&#160;</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>&#160;</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&#38;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>&#160;</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>&#160;</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);
            }
        }
예제 #2
0
        /// <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>&#160;</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>&#160;</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&#38;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>&#160;</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>&#160;</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);
            }
        }
예제 #3
0
        /// <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>&#160;</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);
            }
        }
예제 #4
0
        /// <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));
            }
        }
예제 #5
0
        /// <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));
            }
        }
예제 #6
0
        /// <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>&#160;</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>&#160;</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&#38;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>&#160;</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>&#160;</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);
            }
        }
예제 #7
0
        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();
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
        /// <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>&#160;</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);
            }
        }
예제 #11
0
        /// <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);
        }
예제 #12
0
        /// <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));
        }
예제 #13
0
        /// <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>&#160;</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);
            }
        }
예제 #14
0
        /// <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);
        }