diff --git a/src/Parsers/ASTIdentifier.cpp b/src/Parsers/ASTIdentifier.cpp index 54b5f45f20b..9d6ad1df08d 100644 --- a/src/Parsers/ASTIdentifier.cpp +++ b/src/Parsers/ASTIdentifier.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -109,7 +110,15 @@ void ASTIdentifier::formatImplWithoutAlias(WriteBuffer & ostr, const FormatSetti auto format_element = [&](const String & elem_name) { ostr << (settings.hilite ? hilite_identifier : ""); - settings.writeIdentifier(ostr, elem_name, /*ambiguous=*/false); + if (auto special_delimiter_and_identifier = ParserCompoundIdentifier::splitSpecialDelimiterAndIdentifierIfAny(elem_name)) + { + ostr << special_delimiter_and_identifier->first; + settings.writeIdentifier(ostr, special_delimiter_and_identifier->second, /*ambiguous=*/false); + } + else + { + settings.writeIdentifier(ostr, elem_name, /*ambiguous=*/false); + } ostr << (settings.hilite ? hilite_none : ""); }; diff --git a/src/Parsers/ExpressionElementParsers.cpp b/src/Parsers/ExpressionElementParsers.cpp index 6c2b7587491..b73ee7b688c 100644 --- a/src/Parsers/ExpressionElementParsers.cpp +++ b/src/Parsers/ExpressionElementParsers.cpp @@ -415,6 +415,20 @@ bool ParserCompoundIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected & ex return true; } +std::optional> ParserCompoundIdentifier::splitSpecialDelimiterAndIdentifierIfAny(const String & name) +{ + /// Identifier with special delimiter looks like this: ``. + if (name.size() < 3 + || (name[0] != char(SpecialDelimiter::JSON_PATH_DYNAMIC_TYPE) && name[0] != char(SpecialDelimiter::JSON_PATH_PREFIX)) + || name[1] != '`' || name.back() != '`') + return std::nullopt; + + String identifier; + ReadBufferFromMemory buf(name.data() + 1, name.size() - 1); + readBackQuotedString(identifier, buf); + return std::make_pair(name[0], identifier); +} + ASTPtr createFunctionCast(const ASTPtr & expr_ast, const ASTPtr & type_ast) { diff --git a/src/Parsers/ExpressionElementParsers.h b/src/Parsers/ExpressionElementParsers.h index a8e49f7e560..e3e3e103d16 100644 --- a/src/Parsers/ExpressionElementParsers.h +++ b/src/Parsers/ExpressionElementParsers.h @@ -61,6 +61,16 @@ protected: class ParserCompoundIdentifier : public IParserBase { public: + explicit ParserCompoundIdentifier(bool table_name_with_optional_uuid_ = false, bool allow_query_parameter_ = false, Highlight highlight_type_ = Highlight::identifier) + : table_name_with_optional_uuid(table_name_with_optional_uuid_), allow_query_parameter(allow_query_parameter_), highlight_type(highlight_type_) + { + } + + /// Checks if the identirier is actually a pair of a special delimiter and the identifier in back quotes. + /// For example: :`UInt64` or ^`path` from special JSON subcolumns. + static std::optional> splitSpecialDelimiterAndIdentifierIfAny(const String & name); + +protected: enum class SpecialDelimiter : char { NONE = '\0', @@ -68,12 +78,6 @@ public: JSON_PATH_PREFIX = '^', }; - explicit ParserCompoundIdentifier(bool table_name_with_optional_uuid_ = false, bool allow_query_parameter_ = false, Highlight highlight_type_ = Highlight::identifier) - : table_name_with_optional_uuid(table_name_with_optional_uuid_), allow_query_parameter(allow_query_parameter_), highlight_type(highlight_type_) - { - } - -protected: const char * getName() const override { return "compound identifier"; } bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; bool table_name_with_optional_uuid; diff --git a/tests/queries/0_stateless/03285_json_subcolumns_formatting.reference b/tests/queries/0_stateless/03285_json_subcolumns_formatting.reference new file mode 100644 index 00000000000..1ef2e6b6afb --- /dev/null +++ b/tests/queries/0_stateless/03285_json_subcolumns_formatting.reference @@ -0,0 +1,12 @@ +SELECT json.^sub.object.path +FROM test +SELECT json.^`s^b`.object.path +FROM test +SELECT json.`^sub`.object.path +FROM test +SELECT json.path.:UInt64 +FROM test +SELECT json.path.:`Array(JSON)` +FROM test +SELECT json.path.`:UInt64` +FROM test diff --git a/tests/queries/0_stateless/03285_json_subcolumns_formatting.sh b/tests/queries/0_stateless/03285_json_subcolumns_formatting.sh new file mode 100755 index 00000000000..c3b1d6ae31f --- /dev/null +++ b/tests/queries/0_stateless/03285_json_subcolumns_formatting.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +$CLICKHOUSE_FORMAT --query "select json.^sub.object.path from test"; +$CLICKHOUSE_FORMAT --query "select json.^\`s^b\`.object.path from test"; +$CLICKHOUSE_FORMAT --query "select json.\`^sub\`.object.path from test"; + +$CLICKHOUSE_FORMAT --query "select json.path.:UInt64 from test"; +$CLICKHOUSE_FORMAT --query "select json.path.:\`Array(JSON)\` from test"; +$CLICKHOUSE_FORMAT --query "select json.path.\`:UInt64\` from test"; +