diff --git a/base/common/find_symbols.h b/base/common/find_symbols.h index 65d4a53ceff..a5921b813a1 100644 --- a/base/common/find_symbols.h +++ b/base/common/find_symbols.h @@ -308,7 +308,7 @@ inline void splitInto(To & to, const std::string & what, bool token_compress = f const char * delimiter_or_end = find_first_symbols(pos, end); if (!token_compress || pos < delimiter_or_end) - to.emplace_back(pos, delimiter_or_end); + to.emplace_back(pos, delimiter_or_end - pos); if (delimiter_or_end < end) pos = delimiter_or_end + 1; diff --git a/src/Common/StringUtils/StringUtils.h b/src/Common/StringUtils/StringUtils.h index 50573694b7a..a1e8fb79435 100644 --- a/src/Common/StringUtils/StringUtils.h +++ b/src/Common/StringUtils/StringUtils.h @@ -122,6 +122,11 @@ inline bool isPrintableASCII(char c) return uc >= 32 && uc <= 126; /// 127 is ASCII DEL. } +inline bool isValidIdentifier(const std::string_view & str) +{ + return !str.empty() && isValidIdentifierBegin(str[0]) && std::all_of(str.begin() + 1, str.end(), isWordCharASCII); +} + /// Works assuming isAlphaASCII. inline char toLowerIfAlphaASCII(char c) { diff --git a/src/IO/WriteHelpers.cpp b/src/IO/WriteHelpers.cpp index 9fe194a70c8..a0a2a45c791 100644 --- a/src/IO/WriteHelpers.cpp +++ b/src/IO/WriteHelpers.cpp @@ -68,22 +68,10 @@ void writeException(const Exception & e, WriteBuffer & buf, bool with_stack_trac template static inline void writeProbablyQuotedStringImpl(const StringRef & s, WriteBuffer & buf, F && write_quoted_string) { - if (!s.size || !isValidIdentifierBegin(s.data[0])) - { - write_quoted_string(s, buf); - } + if (isValidIdentifier(std::string_view{s})) + writeString(s, buf); else - { - const char * pos = s.data + 1; - const char * end = s.data + s.size; - for (; pos < end; ++pos) - if (!isWordCharASCII(*pos)) - break; - if (pos != end) - write_quoted_string(s, buf); - else - writeString(s, buf); - } + write_quoted_string(s, buf); } void writeProbablyBackQuotedString(const StringRef & s, WriteBuffer & buf) diff --git a/src/Parsers/ASTSetQuery.cpp b/src/Parsers/ASTSetQuery.cpp new file mode 100644 index 00000000000..fd5409e4bdd --- /dev/null +++ b/src/Parsers/ASTSetQuery.cpp @@ -0,0 +1,22 @@ +#include +#include + + +namespace DB +{ +void ASTSetQuery::formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const +{ + if (is_standalone) + format.ostr << (format.hilite ? hilite_keyword : "") << "SET " << (format.hilite ? hilite_none : ""); + + for (auto it = changes.begin(); it != changes.end(); ++it) + { + if (it != changes.begin()) + format.ostr << ", "; + + formatSettingName(it->name, format.ostr); + format.ostr << " = " << applyVisitor(FieldVisitorToString(), it->value); + } +} + +} diff --git a/src/Parsers/ASTSetQuery.h b/src/Parsers/ASTSetQuery.h index 19277d7f53b..e5c3e31b3c2 100644 --- a/src/Parsers/ASTSetQuery.h +++ b/src/Parsers/ASTSetQuery.h @@ -8,7 +8,6 @@ namespace DB { - /** SET query */ class ASTSetQuery : public IAST @@ -23,19 +22,7 @@ public: ASTPtr clone() const override { return std::make_shared(*this); } - void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override - { - if (is_standalone) - settings.ostr << (settings.hilite ? hilite_keyword : "") << "SET " << (settings.hilite ? hilite_none : ""); - - for (auto it = changes.begin(); it != changes.end(); ++it) - { - if (it != changes.begin()) - settings.ostr << ", "; - - settings.ostr << it->name << " = " << applyVisitor(FieldVisitorToString(), it->value); - } - } + void formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const override; }; } diff --git a/src/Parsers/ASTSettingsProfileElement.cpp b/src/Parsers/ASTSettingsProfileElement.cpp index a7955411347..c0fb2965a2d 100644 --- a/src/Parsers/ASTSettingsProfileElement.cpp +++ b/src/Parsers/ASTSettingsProfileElement.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -31,7 +32,7 @@ void ASTSettingsProfileElement::formatImpl(const FormatSettings & settings, Form return; } - settings.ostr << setting_name; + formatSettingName(setting_name, settings.ostr); if (!value.isNull()) { diff --git a/src/Parsers/ParserSetQuery.cpp b/src/Parsers/ParserSetQuery.cpp index 30d681cb126..b57f5799304 100644 --- a/src/Parsers/ParserSetQuery.cpp +++ b/src/Parsers/ParserSetQuery.cpp @@ -15,7 +15,7 @@ namespace DB /// Parse `name = value`. bool ParserSetQuery::parseNameValuePair(SettingChange & change, IParser::Pos & pos, Expected & expected) { - ParserIdentifier name_p; + ParserCompoundIdentifier name_p; ParserLiteral value_p; ParserToken s_eq(TokenType::Equals); diff --git a/src/Parsers/ParserSettingsProfileElement.cpp b/src/Parsers/ParserSettingsProfileElement.cpp index 39e1f2d3594..f1e66a296e7 100644 --- a/src/Parsers/ParserSettingsProfileElement.cpp +++ b/src/Parsers/ParserSettingsProfileElement.cpp @@ -118,7 +118,7 @@ namespace return IParserBase::wrapParseImpl(pos, [&] { ASTPtr name_ast; - if (!ParserIdentifier{}.parse(pos, name_ast, expected)) + if (!ParserCompoundIdentifier{}.parse(pos, name_ast, expected)) return false; String res_setting_name = getIdentifierName(name_ast); diff --git a/src/Parsers/formatSettingName.cpp b/src/Parsers/formatSettingName.cpp new file mode 100644 index 00000000000..3f30142716e --- /dev/null +++ b/src/Parsers/formatSettingName.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + + +namespace DB +{ + +void formatSettingName(const String & setting_name, std::ostream & out) +{ + if (isValidIdentifier(setting_name)) + { + out << setting_name; + return; + } + + std::vector parts; + splitInto<'.'>(parts, setting_name); + bool all_parts_are_identifiers = std::all_of(parts.begin(), parts.end(), isValidIdentifier); + if (all_parts_are_identifiers && !parts.empty()) + { + bool need_dot = false; + for (const auto & part : parts) + { + if (std::exchange(need_dot, true)) + out << "."; + out << part; + } + return; + } + + out << backQuote(setting_name); +} + +} diff --git a/src/Parsers/formatSettingName.h b/src/Parsers/formatSettingName.h new file mode 100644 index 00000000000..40f14d95b4f --- /dev/null +++ b/src/Parsers/formatSettingName.h @@ -0,0 +1,14 @@ +#pragma once + +#include + + +namespace DB +{ + +/// Outputs built-in or custom setting's name. +/// The function is like backQuoteIfNeed() but didn't quote with backticks +/// if the name consists of identifiers joined with dots. +void formatSettingName(const String & setting_name, std::ostream & out); + +} diff --git a/src/Parsers/ya.make b/src/Parsers/ya.make index ada5975bcf0..06b49f2b5ab 100644 --- a/src/Parsers/ya.make +++ b/src/Parsers/ya.make @@ -44,6 +44,7 @@ SRCS( ASTSampleRatio.cpp ASTSelectQuery.cpp ASTSelectWithUnionQuery.cpp + ASTSetQuery.cpp ASTSetRoleQuery.cpp ASTSettingsProfileElement.cpp ASTShowAccessEntitiesQuery.cpp @@ -61,6 +62,7 @@ SRCS( ExpressionElementParsers.cpp ExpressionListParsers.cpp formatAST.cpp + formatSettingName.cpp IAST.cpp iostream_debug_helpers.cpp IParserBase.cpp diff --git a/tests/queries/0_stateless/01418_custom_settings.reference b/tests/queries/0_stateless/01418_custom_settings.reference index 1e5fd30305a..5f239591218 100644 --- a/tests/queries/0_stateless/01418_custom_settings.reference +++ b/tests/queries/0_stateless/01418_custom_settings.reference @@ -15,3 +15,7 @@ custom_b NULL custom_c UInt64_50000 custom_d Float64_1.11 0 UInt8 + +test String +custom_compound.identifier.v1 \'test\' +CREATE SETTINGS PROFILE s1_01418 SETTINGS custom_compound.identifier.v2 = 100 diff --git a/tests/queries/0_stateless/01418_custom_settings.sql b/tests/queries/0_stateless/01418_custom_settings.sql index 968ec22b538..1c87b143b79 100644 --- a/tests/queries/0_stateless/01418_custom_settings.sql +++ b/tests/queries/0_stateless/01418_custom_settings.sql @@ -23,3 +23,12 @@ SET custom_e = 0; SELECT getSetting('custom_e') as v, toTypeName(v); SET invalid_custom = 8; -- { serverError 115 } -- Setting is neither a builtin nor started with one of the registered prefixes for user-defined settings. + +SELECT ''; +SET custom_compound.identifier.v1 = 'test'; +SELECT getSetting('custom_compound.identifier.v1') as v, toTypeName(v); +SELECT name, value FROM system.settings WHERE name = 'custom_compound.identifier.v1'; + +CREATE SETTINGS PROFILE s1_01418 SETTINGS custom_compound.identifier.v2 = 100; +SHOW CREATE SETTINGS PROFILE s1_01418; +DROP SETTINGS PROFILE s1_01418;