void MySQL_Connection::init()

in jdbc/driver/mysql_connection.cpp [682:1808]


void MySQL_Connection::init(ConnectOptionsMap & properties)
{
  CPP_ENTER_WL(intern->logger, "MySQL_Connection::init");

  intern->is_valid = true;

  MySQL_Uri uri;
  MySQL_Uri::Host_data host;

  sql::SQLString userName;
  sql::SQLString password;
  sql::SQLString defaultCharset("utf8mb4");
  sql::SQLString characterSetResults("utf8mb4");

  sql::SQLString sslKey, sslCert, sslCA, sslCAPath, sslCipher, postInit;
  bool ssl_used = false;
  unsigned long flags = CLIENT_MULTI_RESULTS;

  const int * p_i = nullptr;
  const bool * p_b = nullptr;
  const sql::SQLString * p_s = nullptr;
  bool opt_reconnect = false;
  int  client_exp_pwd = false;
  bool opt_dns_srv = false;
  bool opt_multi_host = false;
#if MYCPPCONN_STATIC_MYSQL_VERSION_ID < 80000
  bool secure_auth= true;
#endif

  /*
    Add default connector connection attributes
  */
  std::map<sql::SQLString, sql::SQLString> default_attr = {
      {"_connector_name", "mysql-connector-cpp"},
      {"_connector_version", MYCPPCONN_DM_VERSION},
      {"_connector_license", MYSQL_CONCPP_LICENSE}};

  for (auto &el : default_attr) {
    proxy->options(sql::mysql::MYSQL_OPT_CONNECT_ATTR_ADD, el.first, el.second);
  }

  sql::ConnectOptionsMap::const_iterator it;

#ifdef TELEMETRY
  // TODO: Use these helpers to reduce code repetition.

  auto get_option_i = [&properties, &p_i](std::string name, bool check = true)
  {
    if (!properties.count(name))
      return false;
    try {
      p_i = properties.at(name).get<int>();
      if (check && !p_i)
        throw sql::InvalidArgumentException{
          "No long long value passed for " + name
        };
      return true;
    }
    catch (sql::InvalidArgumentException&)
    {
      throw sql::InvalidArgumentException{
        "Wrong type passed for " + name + " expected long long"
      };
    }
  };

  auto get_option_b = [&properties, &p_b](std::string name, bool check = true)
  {
    if (!properties.count(name))
      return false;
    try {
      p_b = properties.at(name).get<bool>();
      if (check && !p_b)
        throw sql::InvalidArgumentException{
          "No bool value passed for " + name
        };
      return true;
    }
    catch (sql::InvalidArgumentException&)
    {
      throw sql::InvalidArgumentException{
        "Wrong type passed for " + name + " expected bool"
      };
    }
  };

#if 0
  auto get_option_s = [&properties, &p_s](const char *name, bool check = true)
  {
    if (!properties.count(name))
      return false;
    try {
      p_s = properties.at(name).get<sql::SQLString>();
      if (check && !p_s)
        throw sql::InvalidArgumentException{
          std::string{"No string value passed for "} + name
        };
      return true;
    }
    catch (sql::InvalidArgumentException&)
    {
      throw sql::InvalidArgumentException{
        std::string{"Wrong type passed for "} + name
        + " expected sql::SQLString"
      };
    }
  };
#endif
#endif

  /* Port from options must be set as default for all hosts where port
     is not specified */
  {
    it = properties.find("port");

    if (it != properties.end())	{
      try {
        p_i = (it->second).get< int >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for port expected int");
      }
      if (p_i) {
        uri.setDefaultPort(*p_i);
      } else {
        throw sql::InvalidArgumentException("No long long value passed for port");
      }
    }
  }

  /* Values set in properties individually should have priority over those
     we restore from Uri */
  {
    it = properties.find("hostName");

    if (it != properties.end())	{
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for userName expected sql::SQLString");
      }
      if (p_s) {
        /*
          Parsing uri prior to processing all parameters, so indivudually
          specified parameters precede over those in the uri
        */
        if(!parseUri(*p_s, uri))
          throw sql::InvalidArgumentException("Invalid hostname URI");

      } else {
        throw sql::InvalidArgumentException("No string value passed for hostName");
      }
    }
  }

  /*
    Note: We set pluginDir option early because other options that are plugin
    specific might require loading plugins before they can be set.
  */

  {
    sql::SQLString plugin_dir;
    it = properties.find(OPT_PLUGIN_DIR);
    p_s = nullptr;

    if (it != properties.end()) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for pluginDir expected sql::SQLString");
      }
    }
    else if(!default_plugin_dir.empty()) {
      plugin_dir = default_plugin_dir;
      p_s = &plugin_dir;
    }

    if (p_s) {
      proxy->options(sql::mysql::MYSQL_PLUGIN_DIR, *p_s);
    } else if (it != properties.end()) {
      // Throw only when OPT_PLUGIN_DIR is used, but no value is given
      throw sql::InvalidArgumentException("No string value passed for pluginDir");
    }
  }

  /*
    OPT_OPENTELEMETRY

    We first try to get it as enum constant (integer value). If this does
    not work, we try bool value.
  */

#ifndef TELEMETRY

  if (properties.count(OPT_OPENTELEMETRY))
  {
    throw sql::SQLException{
      "Option OPT_OPENTELEMETRY not yet supported on this platform."
    };
  }

#else

  try {
    if (get_option_i(OPT_OPENTELEMETRY))
    {
      switch (*p_i)
      {
      case OTEL_DISABLED:
      case OTEL_PREFERRED:
        break;
      default:
        throw sql::InvalidArgumentException{
          "Invalid value for OPT_OPENTELEMETRY;"
          " expecting OTEL_DISABLED or OTEL_PREFERRED"
        };
      };

      intern->telemetry.set_mode((enum_opentelemetry_mode)*p_i);
    }
  }
  catch(const sql::InvalidArgumentException&)
  {
    try {

      get_option_b(OPT_OPENTELEMETRY);
      if (*p_b)
        throw sql::InvalidArgumentException{
          "OPT_OPENTELEMETRY can only be set to FALSE"
        };
      intern->telemetry.set_mode(OTEL_DISABLED);
    }
    catch(const sql::InvalidArgumentException&)
    {
      throw sql::InvalidArgumentException{
        "Wrong type passed for OPT_OPENTELEMETRY"
        " expected enum_opentelemetry_mode or bool (FALSE)"
      };
    }
  }

#endif

#define PROCESS_CONN_OPTION(option_type, options_map) \
  process_connection_option< option_type >(it, options_map, sizeof(options_map)/sizeof(String2IntMap), proxy)

  for (it = properties.begin(); it != properties.end(); ++it) {
    if (!it->first.compare(OPT_USERNAME)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for userName expected sql::SQLString");
      }
      if (p_s) {
        userName = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for userName");
      }
    } else if (!it->first.compare(OPT_PASSWORD)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for password expected sql::SQLString");
      }
      if (p_s) {
        password = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for password");
      }
    } else if (!it->first.compare(OPT_PASSWORD1)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for password1 expected sql::SQLString");
      }
      if (p_s) {
        int num = 1;
        proxy->options(sql::mysql::MYSQL_OPT_USER_PASSWORD, num, *p_s);
      } else {
        throw sql::InvalidArgumentException("No string value passed for password1");
      }
    } else if (!it->first.compare(OPT_PASSWORD2)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for password2 expected sql::SQLString");
      }
      if (p_s) {
        int num = 2;
        proxy->options(sql::mysql::MYSQL_OPT_USER_PASSWORD, num, *p_s);
      } else {
        throw sql::InvalidArgumentException("No string value passed for password2");
      }
    } else if (!it->first.compare(OPT_PASSWORD3)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for password3 expected sql::SQLString");
      }
      if (p_s) {
        int num = 3;
        proxy->options(sql::mysql::MYSQL_OPT_USER_PASSWORD, num, *p_s);
      } else {
        throw sql::InvalidArgumentException("No string value passed for password3");
      }
    } else if (!it->first.compare(OPT_PORT)) {
      try {
        p_i = (it->second).get< int >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for port expected int");
      }
      if (p_i) {
        uri.setDefaultPort(*p_i);
      } else {
        throw sql::InvalidArgumentException("No long long value passed for port");
      }
    } else if (!it->first.compare(OPT_SOCKET)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for socket expected sql::SQLString");
      }
      if (p_s) {
        host.setSocket(*p_s);
        uri.setHost(host);
      } else {
        throw sql::InvalidArgumentException("No string value passed for socket");
      }
    } else if (!it->first.compare(OPT_PIPE)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for pipe expected sql::SQLString");
      }
      if (p_s) {
        host.setPipe(*p_s);
        uri.setHost(host);
      } else {
        throw sql::InvalidArgumentException("No string value passed for pipe");
      }
    } else if (!it->first.compare(OPT_SCHEMA)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for schema expected sql::SQLString");
      }
      if (p_s) {
        uri.setSchema(*p_s);
      } else {
        throw sql::InvalidArgumentException("No string value passed for schema");
      }
    } else if (!it->first.compare(OPT_CHARACTER_SET_RESULTS)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for characterSetResults expected sql::SQLString");
      }
      if (p_s) {
        characterSetResults = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for characterSetResults");
      }
    } else if (!it->first.compare(OPT_SSL_KEY) || !it->first.compare("sslKey")) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for ssl-key expected sql::SQLString");
      }
      if (p_s) {
        sslKey = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for ssl-key");
      }
      ssl_used = true;
    } else if (!it->first.compare(OPT_SSL_CERT) || !it->first.compare("sslCert")) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for ssl-cert expected sql::SQLString");
      }
      if (p_s) {
        sslCert = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for ssl-cert");
      }
      ssl_used = true;
    } else if (!it->first.compare(OPT_SSL_CA) || !it->first.compare("sslCA") ) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for ssl-ca expected sql::SQLString");
      }
      if (p_s) {
        sslCA = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for ssl-ca");
      }
      ssl_used = true;
    } else if (!it->first.compare(OPT_SSL_CAPATH) || !it->first.compare("sslCAPath")) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for ssl-capath expected sql::SQLString");
      }
      if (p_s) {
        sslCAPath = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for ssl-capath");
      }
      ssl_used = true;
    } else if (!it->first.compare(OPT_SSL_CIPHER) || !it->first.compare("sslCipher")) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for ssl-cipher expected sql::SQLString");
      }
      if (p_s) {
        sslCipher = *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for ssl-cipher");
      }
      ssl_used = true;
    } else if (!it->first.compare(OPT_TLS_VERSION) || !it->first.compare("OPT_TLS_VERSION")) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_TLS_VERSION expected sql::SQLString");
      }
      if (p_s) {
        try {
          proxy->options(sql::mysql::MYSQL_OPT_TLS_VERSION, *p_s);
        }  catch (const sql::InvalidArgumentException&) {
          //We will not throw error here, but wait for connection error
          //libmysqlclient treats not valid TLS versions as invalid options.
        }

      } else {
        throw sql::InvalidArgumentException("No string value passed for OPT_TLS_VERSION");
      }
    } else if (!it->first.compare(OPT_DEFAULT_STMT_RESULT_TYPE)) {
      try {
        p_i = (it->second).get< int >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for defaultStatementResultType expected sql::SQLString");
      }
      if (!p_i) {
        throw sql::InvalidArgumentException("No long long value passed for defaultStatementResultType");
      }
      do {
        if (static_cast< int >(sql::ResultSet::TYPE_FORWARD_ONLY) == *p_i) break;
        if (static_cast< int >(sql::ResultSet::TYPE_SCROLL_INSENSITIVE) == *p_i) break;
        if (static_cast< int >(sql::ResultSet::TYPE_SCROLL_SENSITIVE) == *p_i) {
          std::ostringstream msg;
          msg << "Invalid value " << *p_i <<
            " for option defaultStatementResultType. TYPE_SCROLL_SENSITIVE is not supported";
          throw sql::InvalidArgumentException(msg.str());
        }
        std::ostringstream msg;
        msg << "Invalid value (" << *p_i << " for option defaultStatementResultType";
        throw sql::InvalidArgumentException(msg.str());
      } while (0);
      intern->defaultStatementResultType = static_cast< sql::ResultSet::enum_type >(*p_i);
    /* The connector is not ready for unbuffered as we need to refetch */
    } else if (!it->first.compare("defaultPreparedStatementResultType")) {
  #if WE_SUPPORT_USE_RESULT_WITH_PS
      try {
        p_i = (it->second).get< int >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for defaultPreparedStatementResultType expected sql::SQLString");
      }
      if (!(p_i)) {
        throw sql::InvalidArgumentException("No long long value passed for defaultPreparedStatementResultType");
      }
      do {
        if (static_cast< int >(sql::ResultSet::TYPE_FORWARD_ONLY) == *p_i) break;
        if (static_cast< int >(sql::ResultSet::TYPE_SCROLL_INSENSITIVE) == *p_i) break;
        if (static_cast< int >(sql::ResultSet::TYPE_SCROLL_SENSITIVE) == *p_i) {
          std::ostringstream msg;
          msg << "Invalid value " << *p_i <<
            " for option defaultPreparedStatementResultType. TYPE_SCROLL_SENSITIVE is not supported";
          throw sql::InvalidArgumentException(msg.str());
        }
        std::ostringstream msg;
        msg << "Invalid value (" << *p_i << " for option defaultPreparedStatementResultType";
        throw sql::InvalidArgumentException(msg.str());
      } while (0);
      intern->defaultPreparedStatementResultType = static_cast< sql::ResultSet::enum_type >(*p_i);
  #else
      throw SQLException("defaultPreparedStatementResultType parameter still not implemented");

  #endif
    } else if (!it->first.compare(OPT_RECONNECT)) {
      try {
        p_b = (it->second).get<bool>();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_RECONNECT expected bool");
      }
      if (!(p_b)) {
        throw sql::InvalidArgumentException("No bool value passed for OPT_RECONNECT");
      }
      opt_reconnect = true;
      intern->reconnect= *p_b;
    } else if (!it->first.compare(OPT_DNS_SRV)) {
      try {
        p_b = (it->second).get<bool>();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_DNS_SRV, expected bool");
      }
      if (!(p_b)) {
        throw sql::InvalidArgumentException("No bool value passed for OPT_DNS_SRV");
      }
      opt_dns_srv = *p_b;
    } else if (!it->first.compare(OPT_MULTI_HOST)) {
      try {
        p_b = (it->second).get<bool>();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_MULTI_HOST, expected bool");
      }
      if (!(p_b)) {
        throw sql::InvalidArgumentException("No bool value passed for OPT_MULTI_HOST");
      }
      opt_multi_host = *p_b;
    } else if (!it->first.compare(OPT_CHARSET_NAME)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_CHARSET_NAME expected sql::SQLString");
      }
      if (!p_s) {
        throw sql::InvalidArgumentException("No SQLString value passed for OPT_CHARSET_NAME");
      }
      defaultCharset = *p_s;
    } else if (!it->first.compare(OPT_NAMED_PIPE)) {
      /* Not sure it is really needed */
      host.setProtocol(NativeAPI::PROTOCOL_PIPE);
      uri.setHost(host);
    } else if (!it->first.compare(OPT_CAN_HANDLE_EXPIRED_PASSWORDS)) {
      try {
        p_b = (it->second).get<bool>();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_CAN_HANDLE_EXPIRED_PASSWORDS expected bool");
      }
      if (!(p_b)) {
        throw sql::InvalidArgumentException("No bool value passed for "
                          "OPT_CAN_HANDLE_EXPIRED_PASSWORDS");
      }
      try {
        client_exp_pwd= proxy->options(MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, (const char*)p_b);
      } catch (sql::InvalidArgumentException& e) {
        std::string errorOption("MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS");
        throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption);
      }
    } else if (!it->first.compare(OPT_POST_INIT_COMMAND)) {
      try {
        p_s = (it->second).get< sql::SQLString >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for postInit expected sql::SQLString");
      }
      if (p_s) {
        postInit= *p_s;
      } else {
        throw sql::InvalidArgumentException("No string value passed for postInit");
      }
    } else if (!it->first.compare(OPT_LEGACY_AUTH)) {
      try {
        p_b = (it->second).get< bool >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for useLegacyAuth expected sql::SQLString");
      }
  #if MYCPPCONN_STATIC_MYSQL_VERSION_ID < 80000
      if (p_b) {
        secure_auth= !*p_b;
      } else {
        throw sql::InvalidArgumentException("No bool value passed for useLegacyAuth");
      }
  #endif
    } else if (!it->first.compare(OPT_CONNECT_ATTR_ADD)) {
      const std::map< sql::SQLString, sql::SQLString > *conVal;
      try {
        conVal= (it->second).get< std::map< sql::SQLString, sql::SQLString > >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_CONNECT_ATTR_ADD expected std::map< sql::SQLString, sql::SQLString >");
      }
      std::map< sql::SQLString, sql::SQLString >::const_iterator conn_attr_it;
      for (conn_attr_it = conVal->begin(); conn_attr_it != conVal->end(); conn_attr_it++) {
        try {
          //Skip default values
          if (default_attr.find(conn_attr_it->first) != default_attr.end())
            continue;
          proxy->options(sql::mysql::MYSQL_OPT_CONNECT_ATTR_ADD,
                         conn_attr_it->first, conn_attr_it->second);
        } catch (sql::InvalidArgumentException& e) {
          std::string errorOption("MYSQL_OPT_CONNECT_ATTR_ADD");
          throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption);
        }
      }
    } else if (!it->first.compare(OPT_CONNECT_ATTR_DELETE)) {
      const std::list< sql::SQLString > *conVal;
      try {
        conVal= (it->second).get< std::list< sql::SQLString > >();
      } catch (sql::InvalidArgumentException&) {
        throw sql::InvalidArgumentException("Wrong type passed for OPT_CONNECT_ATTR_DELETE expected std::list< sql::SQLString >");
      }
      std::list< sql::SQLString >::const_iterator conn_attr_it;
      for (conn_attr_it = conVal->begin(); conn_attr_it != conVal->end(); conn_attr_it++) {
        // Skip default values
        if (default_attr.find(*conn_attr_it) != default_attr.end())
          continue;
        try {
          proxy->options(MYSQL_OPT_CONNECT_ATTR_DELETE, *conn_attr_it);
          } catch (sql::InvalidArgumentException &e) {
            std::string errorOption("MYSQL_OPT_CONNECT_ATTR_DELETE");
            throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption);
          }
      }
    } else if (!it->first.compare(OPT_CONNECT_ATTR_RESET)) {
      proxy->options(MYSQL_OPT_CONNECT_ATTR_RESET, 0);

  #if MYCPPCONN_STATIC_MYSQL_VERSION_ID > 80000

    //Deprecated
    } else if (!it->first.compare("sslVerify")) {

      ssl_mode ssl_mode_val = (it->second).get< bool >() ? SSL_MODE_VERIFY_CA
                                            : SSL_MODE_PREFERRED;
      proxy->options(MYSQL_OPT_SSL_MODE, &ssl_mode_val);

    //Deprecated
    } else if (!it->first.compare("sslEnforce")) {
      ssl_mode ssl_mode_val = (it->second).get< bool >() ? SSL_MODE_REQUIRED
                                                          : SSL_MODE_PREFERRED;
      proxy->options(MYSQL_OPT_SSL_MODE, &ssl_mode_val);

  #endif
    } else if (!it->first.compare(OPT_PLUGIN_DIR)) {
      // Nothing to do here: this option was handeld before the loop

    /* If you need to add new integer connection option that should result in
        calling mysql_optiong - add its mapping to the intOptions array
      */
    } else if (PROCESS_CONN_OPTION(int, intOptions)) {
      // Nothing to do here

    /* For boolean coonection option - add mapping to booleanOptions array */
    } else if (PROCESS_CONN_OPTION(bool, booleanOptions)) {
      // Nothing to do here

    /* For string coonection option - add mapping to stringOptions array */
    } else if (PROCESS_CONN_OPTION(sql::SQLString, stringOptions)) {
      // Nothing to do here
    } else if (read_connection_flag(it, flags)) {
      // Nothing to do here
    } else {
      // TODO: Shouldn't we really create a warning here? as soon as we are able to
      //       create a warning
    }

  } /* End of cycle on connection options map */


  /*
    Setting plugin options.

    Note that plugins are shared between different drivers but each driver and
    each connection can have its own plugin settings. For that reason plugin
    options are set here, before making a connection, to the values specified
    by this connection and driver.

    The guard is needed to prevent overwriting plugin options by another
    connection while this connection is being established.

    Note: If connection options do not specify a value for a plugin option that
    plugin option is set to null which resets it to its default value (which
    could be overwritten by other connections).

    TODO: Move setting of plugin options later in the connection process, after
    any other options which can take long time to set (e.g. OpenSSL options or
    options involving DNS resolution). This is because setting plugin options
    can potentially block other connection and this blocking should be as short
    as possible.
  */

  MySQL_Connection::PluginGuard guard{this};

  /*
    Set option `option` of plugin `plugin_name` of type `plugin_type` to
    the value given by connection option `con_opt_name` if it is specified.
    Otherwise (if the connection option is not specified) reset plugin option
    value to the default value given by `default_val`.

    If plugin option value could not be set throw error with description given
    by `err_msg` (not if the plugin option is set to its default value).

    Note that for most plugin options the default value is restored when
    the option is set to null.
  */

  auto set_plugin_option = [this, &properties] (
    const ::sql::SQLString con_opt_name,
    int plugin_type,
    const ::sql::SQLString & plugin_name,
    const ::sql::SQLString & option,
    const char * err_msg,
    const void* default_val = nullptr
  )
  {
    sql::SQLString *p_s = nullptr;
    const void* val = nullptr;

    auto opt = properties.find(con_opt_name);
    if (opt != properties.end())
    {
      if (stringPluginOptions.count(con_opt_name))
      {
        try
        {
          p_s = (opt->second).get<sql::SQLString>();
          if (!p_s)
            throw sql::InvalidArgumentException{
              "No string value passed for " + con_opt_name
            };
          val = p_s->c_str();
        }
        catch (sql::InvalidArgumentException&)
        {
          throw sql::InvalidArgumentException(
            "Wrong type passed for " + con_opt_name +
            ". Expected sql::SQLString.");
        }
      }
      else if (intPluginOptions.count(con_opt_name))
      {
        try
        {
          val = (opt->second).get<int>();
          if (!val)
            throw sql::InvalidArgumentException{
              "No int value passed for " + con_opt_name
            };
        }
        catch (sql::InvalidArgumentException&)
        {
          throw sql::InvalidArgumentException(
            "Wrong type passed for " + con_opt_name +
            ". Expected int.");
        }
      }
      else
      {
        /*
          We end up here only if below this lambda is called with connection
          option that is not a plugin option (not listed in
          `stringPluginOptions` or `intPluginOptions` -- that should never
          happen.
        */
        assert(false);
      }
    }

    try
    {
      /*
        Note: `val` is null if the connection option was not set. In that case
        we reset plugin option to the default value as given by `default_val`
        parameter. The last argument of `plugin_option()` informs that the
        option set is the default one which is the case when `val` is null.
      */

      proxy->plugin_option(
        plugin_type, plugin_name, option,
        val ? val : default_val, val == nullptr
      );
    }
    catch (sql::InvalidArgumentException &e)
    {
      if (val)
        // Throw only when setting to a non-default value
        throw ::sql::SQLUnsupportedOptionException(err_msg,
        con_opt_name.asStdString());
    }
  };


  set_plugin_option(OPT_OCI_CONFIG_FILE,
    MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
    "authentication_oci_client",
    "oci-config-file",
    "Failed to set config file for authentication_oci_client plugin"
  );

  set_plugin_option(OPT_OCI_CLIENT_CONFIG_PROFILE,
    MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
    "authentication_oci_client",
    "authentication-oci-client-config-profile",
    "Failed to set config profile for authentication_oci_client plugin"
  );

#if defined(_WIN32)
  set_plugin_option(OPT_AUTHENTICATION_KERBEROS_CLIENT_MODE,
    MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
    "authentication_kerberos_client",
    "plugin_authentication_kerberos_client_mode",
    "Failed to set config file for authentication_kerberos_client plugin"
  );
#endif

  set_plugin_option(OPT_OPENID_TOKEN_FILE,
    MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
    "authentication_openid_connect_client",
    "id-token-file",
    "Failed to set token file for authentication_openid_connect_client plugin"
  );

  // Note: The default value for WebAuthN "device" option is 0.

  const int webauthn_device_default_val = 0;

  set_plugin_option(OPT_WEBAUTHN_DEVICE_NUMBER,
    MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
    "authentication_webauthn_client",
    "device",
    "Failed to set a WebAuthn authentication device",
    &webauthn_device_default_val
  );

  /*
    Setting webauthn callback functions.

    The callback is an option of the webauthn authentication plugin that
    is configured on the driver level (as opposed to plugin options above,
    which are configured on per-connection basis). Correctly setting the option
    based on driver configuration is handled by register_webauthn_callback()
    function of PluginGuard class. The option will be set only if needed.

    Note: If register_webauthn_callback() sets a callback then the plugin
    options guard ensures that this callback function is not modified by other
    connections while being used.
  */

  guard.register_webauthn_callback(*static_cast<MySQL_Driver*>(driver));


#undef PROCESS_CONNSTR_OPTION

  for(auto h : uri)
  {

    // Throwing in case of wrong protocol
#ifdef _WIN32
    if (h.Protocol() == NativeAPI::PROTOCOL_SOCKET) {
      throw sql::InvalidArgumentException("Invalid for this platform protocol requested(MYSQL_PROTOCOL_SOCKET)");
    }
#else
    if (h.Protocol() == NativeAPI::PROTOCOL_PIPE) {
      throw sql::InvalidArgumentException("Invalid for this platform protocol requested(MYSQL_PROTOCOL_PIPE)");
    }
#endif

  }

#if MYCPPCONN_STATIC_MYSQL_VERSION_ID < 80000
  try {
    proxy->options(MYSQL_SECURE_AUTH, &secure_auth);
  } catch (sql::InvalidArgumentException& e) {
    std::string errorOption("MYSQL_SECURE_AUTH");
    throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption);
  }
#endif

  try {
    proxy->options(MYSQL_SET_CHARSET_NAME, defaultCharset.c_str());
  } catch (sql::InvalidArgumentException& e) {
    std::string errorOption("MYSQL_SET_CHARSET_NAME");
    throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption);
  }

#define SSL_SET(OPT, VAL) if (VAL.length()) \
    try { \
      proxy->options(OPT, VAL.c_str()); \
    } \
    catch (sql::InvalidArgumentException &e) { \
      std::string errorOption(#OPT); \
      throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption); \
    }

#define SSL_OPTIONS_LIST(X) \
  X(MYSQL_OPT_SSL_KEY, sslKey) \
  X(MYSQL_OPT_SSL_CERT, sslCert) \
  X(MYSQL_OPT_SSL_CA, sslCA) \
  X(MYSQL_OPT_SSL_CAPATH, sslCAPath) \
  X(MYSQL_OPT_SSL_CIPHER, sslCipher)

  if (ssl_used) {
    SSL_OPTIONS_LIST(SSL_SET);
  }

  /*
    Workaround for libmysqlclient... if OPT_TLS_VERSION is used,
    it overwrites OPT_SSL_MODE... setting it again.
  */

  it = properties.find(OPT_SSL_MODE);

  //Use legacy option
  if(it == properties.end())
    it = properties.find("OPT_SSL_MODE");

  if (it != properties.end())
  {
     PROCESS_CONN_OPTION(int, intOptions);
  }

  if(!opt_multi_host && uri.size() > 1)
     throw sql::InvalidArgumentException("Missing option OPT_MULTI_HOST = true");

  if(opt_dns_srv && uri.size() > 1)
    throw sql::InvalidArgumentException("Specifying multiple hostnames with DNS SRV look up is not allowed.");

  CPP_INFO_FMT("OPT_DNS_SRV=%d", opt_dns_srv);

  intern->telemetry.span_start(this);

  try
  {
    auto connect = [this,flags,client_exp_pwd, opt_dns_srv](
                  const std::string &host,
                  const std::string &user,
                  const std::string &pwd,
                  const std::string &schema,
                  uint16_t port,
                  const std::string &socketOrPipe)
    {
      CPP_INFO_FMT("hostName=%s", host.c_str());
      CPP_INFO_FMT("user=%s", user.c_str());
      CPP_INFO_FMT("port=%d", port);
      CPP_INFO_FMT("schema=%s", schema.c_str());
      CPP_INFO_FMT("socket/pipe=%s", socketOrPipe.c_str());

      bool connect_result = !opt_dns_srv ?
                              proxy->connect(host, user, pwd, schema, port,
                                            socketOrPipe, flags)
                              :
                              proxy->connect_dns_srv(host, user, pwd,
                                                    schema, flags);

      if (!connect_result)
      {
        CPP_ERR_FMT("Couldn't connect : %d", proxy->errNo());
        CPP_ERR_FMT("Couldn't connect : (%s)", proxy->sqlstate().c_str());
        CPP_ERR_FMT("Couldn't connect : %s", proxy->error().c_str());
        CPP_ERR_FMT("Couldn't connect : %d:(%s) %s", proxy->errNo(), proxy->sqlstate().c_str(), proxy->error().c_str());

        /* If error is "Password has expired" and application supports it while
          mysql client lib does not */
        std::string error_message;
        unsigned int native_error= proxy->errNo();

        if (native_error == ER_MUST_CHANGE_PASSWORD_LOGIN
            && client_exp_pwd) {

          native_error= deCL_CANT_HANDLE_EXP_PWD;
          error_message= "Your password has expired, but your instance of"
                        " Connector/C++ is not linked against mysql client library that"
                        " allows to reset it. To resolve this you either need to change"
                        " the password with mysql client that is capable to do that,"
                        " or rebuild your instance of Connector/C++ against mysql client"
                        " library that supports resetting of an expired password.";
        } else if(native_error == CR_SSL_CONNECTION_ERROR){
          error_message= proxy->error();
          if(error_message.find("TLS version") != std::string::npos)
          {
            error_message+=", valid versions are: TLSv1.2, TLSv1.3";
          }
        }else {
          error_message= proxy->error();
        }

        sql::SQLException e(error_message, proxy->sqlstate(), native_error);
        throw e;
      }
    };

    if(opt_dns_srv)
    {
      if(uri.size() > 1)
      {
        throw sql::InvalidArgumentException("Using more than one host with DNS SRV lookup is not allowed.");
      }

      if(uri.size() ==0)
      {
        throw sql::InvalidArgumentException("No hostname specified for DNS SRV lookup.");
      }

      host = *uri.begin();

      if(host.Protocol() == NativeAPI::PROTOCOL_SOCKET)
      {
        throw sql::InvalidArgumentException("Using Unix domain sockets with DNS SRV lookup is not allowed.");
      }

      if(host.Protocol() == NativeAPI::PROTOCOL_PIPE)
      {
        throw sql::InvalidArgumentException("Using pipe with DNS SRV lookup is not allowed.");
      }

      if(host.hasPort())
      {
        throw sql::InvalidArgumentException("Specifying a port number with DNS SRV lookup is not allowed.");
      }

    }

    //Connect loop
    {
      bool connected = false;
      std::random_device rd;
      std::mt19937 generator(rd());
      int error=0;
      std::string sql_state;

      while(uri.size() && !connected)
      {
        std::uniform_int_distribution<int> distribution(
              0, uri.size() - 1); // define the range of random numbers

        int pos = distribution(generator);
        auto el = uri.begin();

        std::advance(el, pos);
        proxy->use_protocol(el->Protocol());
        host = *el; // Note: for error reporting

        try {
          connect(el->Host(), userName,
                  password,
                  uri.Schema() /* schema */,
                  el->hasPort() ?  el->Port() : uri.DefaultPort(),
                  el->SocketOrPipe());
          connected = true;

          // Connected. We can set the connection telemetry.
          intern->telemetry.set_attribs(this, *el, properties);
          currentUser = userName;
          break;
        }
        catch (sql::SQLException& e)
        {
          error = e.getErrorCode();
          sql_state = e.getSQLState();
          switch (error)
          {
          case ER_CON_COUNT_ERROR:
          case CR_SOCKET_CREATE_ERROR:
          case CR_CONNECTION_ERROR:
          case CR_CONN_HOST_ERROR:
          case CR_IPSOCK_ERROR:
          case CR_UNKNOWN_HOST:
            //On Network errors, continue
            break;
          default:
            //If SQLSTATE not 08xxx, which is used for network errors
            if(e.getSQLState().compare(0,2, "08") != 0)
            {
              //Re-throw error and do not try another host
              throw;
            }
          }

        }

        uri.erase(el);

      };

      if(!connected)
      {
        std::stringstream err;
        if(opt_dns_srv)
          err << "Unable to connect to any of the hosts of " << host.Host() << " SRV";
        else if (uri.size() >1) {
          err << "Unable to connect to any of the hosts";
        }
        else {
          switch(host.Protocol())
          {
          case NativeAPI::PROTOCOL_SOCKET:
          case NativeAPI::PROTOCOL_PIPE:
            err << "Unable to connect to " << host.SocketOrPipe() ;
            break;
          default:
            err << "Unable to connect to " << host.Host() << ":" << host.Port();
            break;
          }
        }
        proxy.reset();
        throw sql::SQLException(err.str(), sql_state, error);
      }
    }



    if (opt_reconnect) {
      try {
        proxy->options(MYSQL_OPT_RECONNECT, (const char *) &intern->reconnect);
      } catch (sql::InvalidArgumentException& e) {
        std::string errorOption("MYSQL_OPT_RECONNECT");
        throw ::sql::SQLUnsupportedOptionException(e.what(), errorOption);
      }
    }

    setAutoCommit(true);
    // Different Values means we have to set different result set encoding
    if (characterSetResults.compare(defaultCharset)) {
      setSessionVariable("character_set_results", characterSetResults.length() ? characterSetResults:"NULL");
    }
    intern->meta.reset(new MySQL_ConnectionMetaData(service.get(), proxy, intern->logger));

    if (postInit.length() > 0) {
      service->executeUpdate(postInit);
    }
  }
  catch(sql::SQLException &e)
  {
    intern->telemetry.set_error(this, e.what());
    throw;
  }
}