/* {{{ tcpProtocol() -I- */ bool tcpProtocol(MySQL_Uri& uri) { return uri.Protocol() == NativeAPI::PROTOCOL_TCP; }
/* URI formats tcp://[host]:port/schema unix://socket pipe://named_pipe */ bool parseUri(const sql::SQLString & str, MySQL_Uri& uri) { if (!str.compare(0, sizeof(MYURI_SOCKET_PREFIX) - 1, MYURI_SOCKET_PREFIX)) { uri.setSocket(str.substr(sizeof(MYURI_SOCKET_PREFIX) - 1, sql::SQLString::npos)); return true; } if (!str.compare(0, sizeof(MYURI_PIPE_PREFIX) - 1 , MYURI_PIPE_PREFIX)) { uri.setPipe(str.substr(sizeof(MYURI_PIPE_PREFIX) - 1, sql::SQLString::npos)); return true; } sql::SQLString host; size_t start_sep, end_sep; /* i wonder how did it work with "- 1"*/ if (!str.compare(0, sizeof(MYURI_TCP_PREFIX) - 1, MYURI_TCP_PREFIX) ) { host= str.substr(sizeof(MYURI_TCP_PREFIX) - 1, sql::SQLString::npos); } else { /* allowing to have port and schema specified even w/out protocol specifier("tcp://") */ host= str.c_str(); } if (host[0] == MYURI_HOST_BEGIN) { end_sep= host.find(MYURI_HOST_END); /* No closing ] after [*/ if (end_sep == sql::SQLString::npos) { return false; } uri.setHost(host.substr(1, end_sep - 1)); /* Cutting host to continue w/ port and schema reading */ host= host.substr(end_sep + 1); } /* Looking where schema part begins */ start_sep = host.find('/'); if (start_sep != sql::SQLString::npos) { if ((host.length() - start_sep) > 1/*Slash*/) { uri.setSchema(host.substr(start_sep + 1, host.length() - start_sep - 1)); } host= host.substr(0, start_sep); } else { uri.setSchema(""); } /* Looking where port part begins*/ start_sep = host.find_last_of(':', sql::SQLString::npos); if (start_sep != sql::SQLString::npos) { uri.setPort(atoi(host.substr(start_sep + 1, sql::SQLString::npos).c_str())); host = host.substr(0, start_sep); } else { uri.setPort(DEFAULT_TCP_PORT); } /* If host was enclosed in [], it has been already set, and "host" variable is empty */ if (host.length() > 0) { uri.setHost(host); } return true; }
/* {{{ MySQL_Connection::init() -I- */ void MySQL_Connection::init(ConnectOptionsMap & properties) { CPP_ENTER_WL(intern->logger, "MySQL_Connection::init"); intern->is_valid = true; MySQL_Uri uri; sql::SQLString userName; sql::SQLString password; sql::SQLString defaultCharset("utf8"); sql::SQLString characterSetResults("utf8"); sql::SQLString sslKey, sslCert, sslCA, sslCAPath, sslCipher, postInit; bool ssl_used = false; int flags = CLIENT_MULTI_RESULTS; const int * p_i; const bool * p_b; const sql::SQLString * p_s; bool opt_reconnect = false; bool opt_reconnect_value = false; bool client_doesnt_support_exp_pwd = false; /* Values set in properties individually should have priority over those we restore from Uri */ sql::ConnectOptionsMap::const_iterator it = properties.find("hostName"); if (it != properties.end()) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { /* Parsing uri prior to processing all parameters, so indivudually specified parameters precede over those in the uri */ parseUri(*p_s, uri); } else { throw sql::InvalidArgumentException("No string value passed for hostName"); } } #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("userName")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { userName = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for userName"); } } else if (!it->first.compare("password")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { password = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for password"); } } else if (!it->first.compare("port")) { if ((p_i = boost::get< int >(&it->second))) { uri.setPort(static_cast<unsigned int>(*p_i)); } else { throw sql::InvalidArgumentException("No long long value passed for port"); } } else if (!it->first.compare("socket")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { uri.setSocket(*p_s); } else { throw sql::InvalidArgumentException("No string value passed for socket"); } } else if (!it->first.compare("pipe")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { uri.setPipe(*p_s); } else { throw sql::InvalidArgumentException("No string value passed for pipe"); } } else if (!it->first.compare("schema")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { uri.setSchema(*p_s); } else { throw sql::InvalidArgumentException("No string value passed for schema"); } } else if (!it->first.compare("characterSetResults")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { characterSetResults = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for characterSetResults"); } } else if (!it->first.compare("sslKey")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { sslKey = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for sslKey"); } ssl_used = true; } else if (!it->first.compare("sslCert")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { sslCert = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for sslCert"); } ssl_used = true; } else if (!it->first.compare("sslCA")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { sslCA = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for sslCA"); } ssl_used = true; } else if (!it->first.compare("sslCAPath")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { sslCAPath = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for sslCAPath"); } ssl_used = true; } else if (!it->first.compare("sslCipher")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { sslCipher = *p_s; } else { throw sql::InvalidArgumentException("No string value passed for sslCipher"); } ssl_used = true; } else if (!it->first.compare("defaultStatementResultType")) { if (!(p_i = boost::get< int >(&it->second))) { 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 if (!(p_i = boost::get< int >(&it->second))) { 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("metadataUseInfoSchema")) { if ((p_b = boost::get<bool>(&it->second))) { intern->metadata_use_info_schema = *p_b; } else { throw sql::InvalidArgumentException("No bool value passed for metadataUseInfoSchema"); } } else if (!it->first.compare("OPT_RECONNECT")) { if (!(p_b = boost::get<bool>(&it->second))) { throw sql::InvalidArgumentException("No bool value passed for OPT_RECONNECT"); } opt_reconnect = true; opt_reconnect_value = *p_b; } else if (!it->first.compare("OPT_CHARSET_NAME")) { if (!(p_s = boost::get< sql::SQLString >(&it->second))) { 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 */ uri.setProtocol(NativeAPI::PROTOCOL_PIPE); } else if (!it->first.compare("OPT_CAN_HANDLE_EXPIRED_PASSWORDS")) { /* We need to know client version at runtime */ long client_ver= proxy->get_client_version(); if (proxy->get_client_version() < 50610) { // TODO: I think we should throw a warning here /* We only need this flag set if application has said it supports expired password mode */ client_doesnt_support_exp_pwd= true; } else { if (!(p_b = boost::get< bool >(&it->second))) { throw sql::InvalidArgumentException("No bool value passed for " "OPT_CAN_HANDLE_EXPIRED_PASSWORDS"); } /* We do not care here about server version */ proxy->options(MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, (const char*)p_b); } } else if (!it->first.compare("postInit")) { if ((p_s = boost::get< sql::SQLString >(&it->second))) { postInit= *p_s; } else { throw sql::InvalidArgumentException("No string value passed for postInit"); } /* 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 */ #undef PROCESS_CONNSTR_OPTION /* libmysql shouldn't think it is too smart */ if (tcpProtocol(uri) && !uri.Host().compare(util::LOCALHOST)) { uri.setHost("127.0.0.1"); } // Throwing in case of wrong protocol #ifdef _WIN32 if (uri.Protocol() == NativeAPI::PROTOCOL_SOCKET) { throw sql::InvalidArgumentException("Invalid for this platform protocol requested(MYSQL_PROTOCOL_SOCKET)"); } #else if (uri.Protocol() == NativeAPI::PROTOCOL_PIPE) { throw sql::InvalidArgumentException("Invalid for this platform protocol requested(MYSQL_PROTOCOL_PIPE)"); } #endif proxy->use_protocol(uri.Protocol()); { const char tmp_bool = 1; proxy->options(MYSQL_SECURE_AUTH, &tmp_bool); } proxy->options(MYSQL_SET_CHARSET_NAME, defaultCharset.c_str()); if (ssl_used) { /* According to the docs, always returns 0 */ proxy->ssl_set(sslKey.c_str(), sslCert.c_str(), sslCA.c_str(), sslCAPath.c_str(), sslCipher.c_str()); } CPP_INFO_FMT("hostName=%s", uri.Host().c_str()); CPP_INFO_FMT("user=%s", userName.c_str()); CPP_INFO_FMT("port=%d", uri.Port()); CPP_INFO_FMT("schema=%s", uri.Schema().c_str()); CPP_INFO_FMT("socket/pipe=%s", uri.SocketOrPipe().c_str()); if (!proxy->connect(uri.Host(), userName, password, uri.Schema() /* schema */, uri.Port(), uri.SocketOrPipe() /*socket or named pipe */, flags)) { 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; int native_error= proxy->errNo(); if (native_error == ER_MUST_CHANGE_PASSWORD_LOGIN && client_doesnt_support_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 { error_message= proxy->error(); } sql::SQLException e(error_message, proxy->sqlstate(), native_error); proxy.reset(); throw e; } if (opt_reconnect) { proxy->options(MYSQL_OPT_RECONNECT, (const char *) &opt_reconnect_value); } setAutoCommit(true); setTransactionIsolation(sql::TRANSACTION_REPEATABLE_READ); // 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); } }