2018-07-25 16:08:23 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <Common/Exception.h>
|
2019-02-02 14:54:50 +00:00
|
|
|
#include <Common/NamePrompter.h>
|
2018-07-25 16:08:23 +00:00
|
|
|
#include <Core/Types.h>
|
|
|
|
#include <Poco/String.h>
|
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If stored objects may have several names (aliases)
|
2020-06-14 07:44:02 +00:00
|
|
|
* this interface may be helpful
|
|
|
|
* template parameter is available as Value
|
|
|
|
*/
|
|
|
|
template <typename ValueType>
|
|
|
|
class IFactoryWithAliases : public IHints<2, IFactoryWithAliases<ValueType>>
|
2018-07-25 16:08:23 +00:00
|
|
|
{
|
|
|
|
protected:
|
2020-06-14 07:44:02 +00:00
|
|
|
using Value = ValueType;
|
2018-07-25 16:08:23 +00:00
|
|
|
|
|
|
|
String getAliasToOrName(const String & name) const
|
|
|
|
{
|
|
|
|
if (aliases.count(name))
|
|
|
|
return aliases.at(name);
|
|
|
|
else if (String name_lowercase = Poco::toLower(name); case_insensitive_aliases.count(name_lowercase))
|
|
|
|
return case_insensitive_aliases.at(name_lowercase);
|
|
|
|
else
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// For compatibility with SQL, it's possible to specify that certain function name is case insensitive.
|
|
|
|
enum CaseSensitiveness
|
|
|
|
{
|
|
|
|
CaseSensitive,
|
|
|
|
CaseInsensitive
|
|
|
|
};
|
|
|
|
|
2020-06-14 07:44:02 +00:00
|
|
|
/** Register additional name for value
|
|
|
|
* real_name have to be already registered.
|
|
|
|
*/
|
2018-07-25 16:08:23 +00:00
|
|
|
void registerAlias(const String & alias_name, const String & real_name, CaseSensitiveness case_sensitiveness = CaseSensitive)
|
|
|
|
{
|
2020-06-14 07:44:02 +00:00
|
|
|
const auto & creator_map = getMap();
|
|
|
|
const auto & case_insensitive_creator_map = getCaseInsensitiveMap();
|
2018-07-25 16:08:23 +00:00
|
|
|
const String factory_name = getFactoryName();
|
|
|
|
|
|
|
|
String real_dict_name;
|
|
|
|
if (creator_map.count(real_name))
|
|
|
|
real_dict_name = real_name;
|
|
|
|
else if (auto real_name_lowercase = Poco::toLower(real_name); case_insensitive_creator_map.count(real_name_lowercase))
|
|
|
|
real_dict_name = real_name_lowercase;
|
|
|
|
else
|
|
|
|
throw Exception(factory_name + ": can't create alias '" + alias_name + "', the real name '" + real_name + "' is not registered",
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
String alias_name_lowercase = Poco::toLower(alias_name);
|
|
|
|
|
|
|
|
if (creator_map.count(alias_name) || case_insensitive_creator_map.count(alias_name_lowercase))
|
|
|
|
throw Exception(
|
|
|
|
factory_name + ": the alias name '" + alias_name + "' is already registered as real name", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
if (case_sensitiveness == CaseInsensitive)
|
|
|
|
if (!case_insensitive_aliases.emplace(alias_name_lowercase, real_dict_name).second)
|
|
|
|
throw Exception(
|
|
|
|
factory_name + ": case insensitive alias name '" + alias_name + "' is not unique", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
if (!aliases.emplace(alias_name, real_dict_name).second)
|
|
|
|
throw Exception(factory_name + ": alias name '" + alias_name + "' is not unique", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
2019-04-03 11:13:22 +00:00
|
|
|
std::vector<String> getAllRegisteredNames() const override
|
2018-07-25 16:08:23 +00:00
|
|
|
{
|
|
|
|
std::vector<String> result;
|
|
|
|
auto getter = [](const auto & pair) { return pair.first; };
|
2020-06-14 07:44:02 +00:00
|
|
|
std::transform(getMap().begin(), getMap().end(), std::back_inserter(result), getter);
|
2018-07-25 16:08:23 +00:00
|
|
|
std::transform(aliases.begin(), aliases.end(), std::back_inserter(result), getter);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isCaseInsensitive(const String & name) const
|
|
|
|
{
|
|
|
|
String name_lowercase = Poco::toLower(name);
|
2020-06-14 07:44:02 +00:00
|
|
|
return getCaseInsensitiveMap().count(name_lowercase) || case_insensitive_aliases.count(name_lowercase);
|
2018-07-25 16:08:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const String & aliasTo(const String & name) const
|
|
|
|
{
|
|
|
|
if (auto it = aliases.find(name); it != aliases.end())
|
|
|
|
return it->second;
|
2018-08-10 04:02:56 +00:00
|
|
|
else if (auto jt = case_insensitive_aliases.find(Poco::toLower(name)); jt != case_insensitive_aliases.end())
|
|
|
|
return jt->second;
|
2018-07-25 16:08:23 +00:00
|
|
|
|
|
|
|
throw Exception(getFactoryName() + ": name '" + name + "' is not alias", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAlias(const String & name) const
|
|
|
|
{
|
|
|
|
return aliases.count(name) || case_insensitive_aliases.count(name);
|
|
|
|
}
|
|
|
|
|
2019-04-04 06:23:12 +00:00
|
|
|
virtual ~IFactoryWithAliases() override {}
|
2018-07-25 16:08:23 +00:00
|
|
|
|
|
|
|
private:
|
2020-06-14 07:44:02 +00:00
|
|
|
using InnerMap = std::unordered_map<String, Value>; // name -> creator
|
2018-07-25 16:08:23 +00:00
|
|
|
using AliasMap = std::unordered_map<String, String>; // alias -> original type
|
|
|
|
|
2020-06-14 07:44:02 +00:00
|
|
|
virtual const InnerMap & getMap() const = 0;
|
|
|
|
virtual const InnerMap & getCaseInsensitiveMap() const = 0;
|
2018-07-25 16:08:23 +00:00
|
|
|
virtual String getFactoryName() const = 0;
|
|
|
|
|
|
|
|
/// Alias map to data_types from previous two maps
|
|
|
|
AliasMap aliases;
|
|
|
|
|
|
|
|
/// Case insensitive aliases
|
|
|
|
AliasMap case_insensitive_aliases;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|