blob: 17165e2154769c3d5de58881c24bea2349cb6536 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vector>
#include "ignite/common/utils.h"
#include "ignite/odbc/utility.h"
#include "ignite/odbc/odbc_error.h"
#include "ignite/odbc/ssl/ssl_mode.h"
#include "ignite/odbc/config/connection_string_parser.h"
#include "ignite/odbc/config/config_tools.h"
#include "ignite/odbc/nested_tx_mode.h"
namespace ignite
{
namespace odbc
{
namespace config
{
const std::string ConnectionStringParser::Key::dsn = "dsn";
const std::string ConnectionStringParser::Key::driver = "driver";
const std::string ConnectionStringParser::Key::schema = "schema";
const std::string ConnectionStringParser::Key::address = "address";
const std::string ConnectionStringParser::Key::server = "server";
const std::string ConnectionStringParser::Key::port = "port";
const std::string ConnectionStringParser::Key::distributedJoins = "distributed_joins";
const std::string ConnectionStringParser::Key::enforceJoinOrder = "enforce_join_order";
const std::string ConnectionStringParser::Key::protocolVersion = "protocol_version";
const std::string ConnectionStringParser::Key::pageSize = "page_size";
const std::string ConnectionStringParser::Key::replicatedOnly = "replicated_only";
const std::string ConnectionStringParser::Key::collocated = "collocated";
const std::string ConnectionStringParser::Key::lazy = "lazy";
const std::string ConnectionStringParser::Key::skipReducerOnUpdate = "skip_reducer_on_update";
const std::string ConnectionStringParser::Key::sslMode = "ssl_mode";
const std::string ConnectionStringParser::Key::sslKeyFile = "ssl_key_file";
const std::string ConnectionStringParser::Key::sslCertFile = "ssl_cert_file";
const std::string ConnectionStringParser::Key::sslCaFile = "ssl_ca_file";
const std::string ConnectionStringParser::Key::user = "user";
const std::string ConnectionStringParser::Key::password = "password";
const std::string ConnectionStringParser::Key::nestedTxMode = "nested_tx_mode";
ConnectionStringParser::ConnectionStringParser(Configuration& cfg):
cfg(cfg)
{
// No-op.
}
ConnectionStringParser::~ConnectionStringParser()
{
// No-op.
}
void ConnectionStringParser::ParseConnectionString(const char* str, size_t len, char delimiter,
diagnostic::DiagnosticRecordStorage* diag)
{
std::string connect_str(str, len);
while (connect_str.rbegin() != connect_str.rend() && *connect_str.rbegin() == 0)
connect_str.erase(connect_str.size() - 1);
while (!connect_str.empty())
{
size_t attr_begin = connect_str.rfind(delimiter);
if (attr_begin == std::string::npos)
attr_begin = 0;
else
++attr_begin;
size_t attr_eq_pos = connect_str.rfind('=');
if (attr_eq_pos == std::string::npos)
attr_eq_pos = 0;
if (attr_begin < attr_eq_pos)
{
const char* key_begin = connect_str.data() + attr_begin;
const char* key_end = connect_str.data() + attr_eq_pos;
const char* value_begin = connect_str.data() + attr_eq_pos + 1;
const char* value_end = connect_str.data() + connect_str.size();
std::string key = common::StripSurroundingWhitespaces(key_begin, key_end);
std::string value = common::StripSurroundingWhitespaces(value_begin, value_end);
if (value[0] == '{' && value[value.size() - 1] == '}')
value = value.substr(1, value.size() - 2);
HandleAttributePair(key, value, diag);
}
if (!attr_begin)
break;
connect_str.erase(attr_begin - 1);
}
}
void ConnectionStringParser::ParseConnectionString(const std::string& str,
diagnostic::DiagnosticRecordStorage* diag)
{
ParseConnectionString(str.data(), str.size(), ';', diag);
}
void ConnectionStringParser::ParseConfigAttributes(const char* str,
diagnostic::DiagnosticRecordStorage* diag)
{
size_t len = 0;
// Getting list length. List is terminated by two '\0'.
while (str[len] || str[len + 1])
++len;
++len;
ParseConnectionString(str, len, '\0', diag);
}
void ConnectionStringParser::HandleAttributePair(const std::string &key, const std::string &value,
diagnostic::DiagnosticRecordStorage* diag)
{
std::string lKey = common::ToLower(key);
if (lKey == Key::dsn)
{
cfg.SetDsn(value);
}
else if (lKey == Key::schema)
{
cfg.SetSchema(value);
}
else if (lKey == Key::address)
{
std::vector<EndPoint> endPoints;
ParseAddress(value, endPoints, diag);
cfg.SetAddresses(endPoints);
}
else if (lKey == Key::server)
{
cfg.SetHost(value);
}
else if (lKey == Key::port)
{
if (value.empty())
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Port attribute value is empty. Using default value.", key, value));
}
return;
}
if (!common::AllOf(value.begin(), value.end(), isdigit))
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Port attribute value contains unexpected characters."
" Using default value.", key, value));
}
return;
}
if (value.size() >= sizeof("65535"))
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Port attribute value is too large. Using default value.", key, value));
}
return;
}
int32_t numValue = 0;
std::stringstream conv;
conv << value;
conv >> numValue;
if (numValue <= 0 || numValue > 0xFFFF)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Port attribute value is out of range. "
"Using default value.", key, value));
}
return;
}
cfg.SetTcpPort(static_cast<uint16_t>(numValue));
}
else if (lKey == Key::distributedJoins)
{
BoolParseResult::Type res = StringToBool(value);
if (res == BoolParseResult::AI_UNRECOGNIZED)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Unrecognized bool value. Using default value.", key, value));
}
return;
}
cfg.SetDistributedJoins(res == BoolParseResult::AI_TRUE);
}
else if (lKey == Key::enforceJoinOrder)
{
BoolParseResult::Type res = StringToBool(value);
if (res == BoolParseResult::AI_UNRECOGNIZED)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Unrecognized bool value. Using default value.", key, value));
}
return;
}
cfg.SetEnforceJoinOrder(res == BoolParseResult::AI_TRUE);
}
else if (lKey == Key::protocolVersion)
{
try
{
ProtocolVersion version = ProtocolVersion::FromString(value);
if (!version.IsSupported())
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
"Specified version is not supported. Default value used.");
}
return;
}
cfg.SetProtocolVersion(version);
}
catch (IgniteError& err)
{
if (diag)
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED, err.GetText());
}
}
else if (lKey == Key::pageSize)
{
if (!common::AllOf(value.begin(), value.end(), isdigit))
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Page size attribute value contains unexpected characters."
" Using default value.", key, value));
}
return;
}
if (value.size() >= sizeof("4294967295"))
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Page size attribute value is too large."
" Using default value.", key, value));
}
return;
}
int64_t numValue = 0;
std::stringstream conv;
conv << value;
conv >> numValue;
if (numValue <= 0 || numValue > 0xFFFFFFFFLL)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Page size attribute value is out of range."
" Using default value.", key, value));
}
return;
}
cfg.SetPageSize(static_cast<int32_t>(numValue));
}
else if (lKey == Key::replicatedOnly)
{
BoolParseResult::Type res = StringToBool(value);
if (res == BoolParseResult::AI_UNRECOGNIZED)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Unrecognized bool value. Using default value.", key, value));
}
return;
}
cfg.SetReplicatedOnly(res == BoolParseResult::AI_TRUE);
}
else if (lKey == Key::collocated)
{
BoolParseResult::Type res = StringToBool(value);
if (res == BoolParseResult::AI_UNRECOGNIZED)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Unrecognized bool value. Using default value.", key, value));
}
return;
}
cfg.SetCollocated(res == BoolParseResult::AI_TRUE);
}
else if (lKey == Key::lazy)
{
BoolParseResult::Type res = StringToBool(value);
if (res == BoolParseResult::AI_UNRECOGNIZED)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Unrecognized bool value. Defaulting to 'false'.", key, value));
}
return;
}
cfg.SetLazy(res == BoolParseResult::AI_TRUE);
}
else if (lKey == Key::skipReducerOnUpdate)
{
BoolParseResult::Type res = StringToBool(value);
if (res == BoolParseResult::AI_UNRECOGNIZED)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
MakeErrorMessage("Unrecognized bool value. Defaulting to 'false'.", key, value));
}
return;
}
cfg.SetSkipReducerOnUpdate(res == BoolParseResult::AI_TRUE);
}
else if (lKey == Key::sslMode)
{
ssl::SslMode::Type mode = ssl::SslMode::FromString(value);
if (mode == ssl::SslMode::UNKNOWN)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
"Specified SSL mode is not supported. Default value used ('disable').");
}
return;
}
cfg.SetSslMode(mode);
}
else if (lKey == Key::sslKeyFile)
{
cfg.SetSslKeyFile(value);
}
else if (lKey == Key::sslCertFile)
{
cfg.SetSslCertFile(value);
}
else if (lKey == Key::sslCaFile)
{
cfg.SetSslCaFile(value);
}
else if (lKey == Key::driver)
{
cfg.SetDriver(value);
}
else if (lKey == Key::user)
{
cfg.SetUser(value);
}
else if (lKey == Key::password)
{
cfg.SetPassword(value);
}
else if (lKey == Key::nestedTxMode)
{
NestedTxMode::Type mode = NestedTxMode::FromString(value);
if (mode == NestedTxMode::AI_UNKNOWN)
{
if (diag)
{
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED,
"Specified nested transaction mode is not supported. Default value used ('error').");
}
return;
}
cfg.SetNestedTxMode(mode);
}
else if (diag)
{
std::stringstream stream;
stream << "Unknown attribute: '" << key << "'. Ignoring.";
diag->AddStatusRecord(SqlState::S01S02_OPTION_VALUE_CHANGED, stream.str());
}
}
ConnectionStringParser::BoolParseResult::Type ConnectionStringParser::StringToBool(const std::string& value)
{
std::string lower = common::ToLower(value);
if (lower == "true")
return BoolParseResult::AI_TRUE;
if (lower == "false")
return BoolParseResult::AI_FALSE;
return BoolParseResult::AI_UNRECOGNIZED;
}
std::string ConnectionStringParser::MakeErrorMessage(const std::string& msg, const std::string& key,
const std::string& value)
{
std::stringstream stream;
stream << msg << " [key='" << key << "', value='" << value << "']";
return stream.str();
}
}
}
}