/* {{{ MySQL_Connection::prepareStatement() -I- */
sql::PreparedStatement *
MySQL_Connection::prepareStatement(const sql::SQLString& sql)
{
	CPP_ENTER_WL(intern->logger, "MySQL_Connection::prepareStatement");
	CPP_INFO_FMT("query=%s", sql.c_str());
	checkClosed();
	boost::shared_ptr< NativeAPI::NativeStatementWrapper > stmt;

	//TODO change - probably no need to catch and throw here. Logging can be done inside proxy
	try {
		 stmt.reset(&proxy->stmt_init());
	} catch (sql::SQLException& e) {
		CPP_ERR_FMT("No statement : %d:(%s) %s", proxy->errNo(), proxy->sqlstate().c_str(), proxy->error().c_str());
		throw e;
	}

	if (stmt->prepare(sql)) {
		CPP_ERR_FMT("Cannot prepare %d:(%s) %s", stmt->errNo(), stmt->sqlstate().c_str(), stmt->error().c_str());
		sql::SQLException e(stmt->error(), stmt->sqlstate(), stmt->errNo());
		stmt.reset();
		throw e;
	}

	return new MySQL_Prepared_Statement(stmt, this, intern->defaultPreparedStatementResultType, intern->logger);
}
예제 #2
0
/* {{{ MySQL_Connection::getClientOption() -I- */
void
MySQL_Connection::getClientOption(const sql::SQLString & optionName, void * optionValue)
{
	CPP_ENTER_WL(intern->logger, "MySQL_Connection::getClientOption");
	if (!optionName.compare("metadataUseInfoSchema")) {
		*(static_cast<bool *>(optionValue)) = intern->metadata_use_info_schema;
	} else if (!optionName.compare("defaultStatementResultType")) {
		*(static_cast<int *>(optionValue)) = intern->defaultStatementResultType;
	} else if (!optionName.compare("defaultPreparedStatementResultType")) {
		*(static_cast<int *>(optionValue)) = intern->defaultPreparedStatementResultType;
	} else if (!optionName.compare("multiByteMinLength")) {
		MY_CHARSET_INFO cs;
		proxy->get_character_set_info(&cs);
		*(static_cast<int *>(optionValue)) = cs.mbminlen;
	} else if (!optionName.compare("multiByteMaxLength")) {
		MY_CHARSET_INFO cs;
		proxy->get_character_set_info(&cs);
		*(static_cast<int *>(optionValue)) = cs.mbmaxlen;
	/* mysql_get_option() was added in mysql 5.7.3 version */
	} else if ( proxy->get_server_version() >= 50703 ) {
		try {
			if (GET_CONN_OPTION(optionName, optionValue, intOptions)) {
				return;
			} else if (GET_CONN_OPTION(optionName, optionValue, booleanOptions)) {
				return;
			} else if (GET_CONN_OPTION(optionName, optionValue, stringOptions)) {
				return;
			}
		} catch (sql::SQLUnsupportedOptionException& e) {
			CPP_ERR_FMT("Unsupported option : %d:(%s) %s", proxy->errNo(), proxy->sqlstate().c_str(), proxy->error().c_str());
			throw e;
		}
	}
}
예제 #3
0
        /* {{{ MySQL_ResultBind::bindResult() -I- */
        void MySQL_ResultBind::bindResult()
        {
            CPP_ENTER("MySQL_Prepared_Statement::bindResult");
            for (unsigned int i = 0; i < num_fields; ++i) {
                delete[](char*) rbind[i].buffer;
            }
            rbind.reset(NULL);
            is_null.reset(NULL);
            err.reset(NULL);
            len.reset(NULL);

            num_fields = proxy->field_count();
            if (!num_fields) {
                return;
            }

            rbind.reset(new MYSQL_BIND[num_fields]);
            memset(rbind.get(), 0, sizeof(MYSQL_BIND) * num_fields);

            is_null.reset(new my_bool[num_fields]);
            memset(is_null.get(), 0, sizeof(my_bool) * num_fields);

            err.reset(new my_bool[num_fields]);
            memset(err.get(), 0, sizeof(my_bool) * num_fields);

            len.reset(new unsigned long[num_fields]);
            memset(len.get(), 0, sizeof(unsigned long) * num_fields);

            std::auto_ptr< NativeAPI::NativeResultsetWrapper > resultMeta(proxy->result_metadata());

            for (unsigned int i = 0; i < num_fields; ++i) {
                MYSQL_FIELD* field = resultMeta->fetch_field();

                struct st_buffer_size_type p = allocate_buffer_for_field(field);
                rbind[i].buffer_type = p.type;
                rbind[i].buffer		= p.buffer;
                rbind[i].buffer_length = static_cast<unsigned long>(p.size);
                rbind[i].length		= &len[i];
                rbind[i].is_null	= &is_null[i];
                rbind[i].error		= &err[i];
                rbind[i].is_unsigned = field->flags & UNSIGNED_FLAG;
            }
            if (proxy->bind_result(rbind.get())) {
                CPP_ERR_FMT("Couldn't bind : %d:(%s) %s", proxy->errNo(), proxy->sqlstate().c_str(), proxy->error().c_str());
                sql::mysql::util::throwSQLException(*proxy.get());
            }
        }
/* {{{ 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);
	}
}