diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index bf705c6e2639..3c39e1181ce2 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -4678,3 +4678,36 @@ The default value is `false`. ``` xml true ``` + +## print_pretty_type_names {#print_pretty_type_names} + +Allows to print deep-nested type names in a pretty way with indents in `DESCRIBE` query and in `toTypeName()` function. + +Example: + +```sql +CREATE TABLE test (a Tuple(b String, c Tuple(d Nullable(UInt64), e Array(UInt32), f Array(Tuple(g String, h Map(String, Array(Tuple(i String, j UInt64))))), k Date), l Nullable(String))) ENGINE=Memory; +DESCRIBE TABLE test FORMAT TSVRaw SETTINGS print_pretty_type_names=1; +``` + +``` +a Tuple( + b String, + c Tuple( + d Nullable(UInt64), + e Array(UInt32), + f Array(Tuple( + g String, + h Map( + String, + Array(Tuple( + i String, + j UInt64 + )) + ) + )), + k Date + ), + l Nullable(String) +) +``` diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 2df702559f1e..3b4975c3d64d 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -806,6 +806,7 @@ class IColumn; M(Timezone, session_timezone, "", "This setting can be removed in the future due to potential caveats. It is experimental and is not suitable for production usage. The default timezone for current session or query. The server default timezone if empty.", 0) \ M(Bool, allow_create_index_without_type, false, "Allow CREATE INDEX query without TYPE. Query will be ignored. Made for SQL compatibility tests.", 0) \ M(Bool, create_index_ignore_unique, false, "Ignore UNIQUE keyword in CREATE UNIQUE INDEX. Made for SQL compatibility tests.", 0) \ + M(Bool, print_pretty_type_names, false, "Print pretty type names in DESCRIBE query and toTypeName() function", 0) \ // End of COMMON_SETTINGS // Please add settings related to formats into the FORMAT_FACTORY_SETTINGS, move obsolete settings to OBSOLETE_SETTINGS and obsolete format settings to OBSOLETE_FORMAT_SETTINGS. diff --git a/src/DataTypes/DataTypeArray.cpp b/src/DataTypes/DataTypeArray.cpp index e31f10046b74..24cd759e2a58 100644 --- a/src/DataTypes/DataTypeArray.cpp +++ b/src/DataTypes/DataTypeArray.cpp @@ -13,6 +13,9 @@ #include #include +#include +#include + namespace DB { @@ -59,6 +62,13 @@ size_t DataTypeArray::getNumberOfDimensions() const return 1 + nested_array->getNumberOfDimensions(); /// Every modern C++ compiler optimizes tail recursion. } +String DataTypeArray::doGetPrettyName(size_t indent) const +{ + WriteBufferFromOwnString s; + s << "Array(" << nested->getPrettyName(indent) << ')'; + return s.str(); +} + static DataTypePtr create(const ASTPtr & arguments) { diff --git a/src/DataTypes/DataTypeArray.h b/src/DataTypes/DataTypeArray.h index 68b574b8ded8..3a83babbc23c 100644 --- a/src/DataTypes/DataTypeArray.h +++ b/src/DataTypes/DataTypeArray.h @@ -29,6 +29,8 @@ class DataTypeArray final : public IDataType return "Array(" + nested->getName() + ")"; } + std::string doGetPrettyName(size_t indent) const override; + const char * getFamilyName() const override { return "Array"; diff --git a/src/DataTypes/DataTypeMap.cpp b/src/DataTypes/DataTypeMap.cpp index 90561857fad9..acd26ca338bc 100644 --- a/src/DataTypes/DataTypeMap.cpp +++ b/src/DataTypes/DataTypeMap.cpp @@ -82,6 +82,16 @@ std::string DataTypeMap::doGetName() const return s.str(); } +std::string DataTypeMap::doGetPrettyName(size_t indent) const +{ + WriteBufferFromOwnString s; + s << "Map(\n" + << fourSpaceIndent(indent + 1) << key_type->getPrettyName(indent + 1) << ",\n" + << fourSpaceIndent(indent + 1) << value_type->getPrettyName(indent + 1) << '\n' + << fourSpaceIndent(indent) << ')'; + return s.str(); +} + MutableColumnPtr DataTypeMap::createColumn() const { return ColumnMap::create(nested->createColumn()); diff --git a/src/DataTypes/DataTypeMap.h b/src/DataTypes/DataTypeMap.h index 294c5d7ac77f..619815fdf20f 100644 --- a/src/DataTypes/DataTypeMap.h +++ b/src/DataTypes/DataTypeMap.h @@ -29,6 +29,7 @@ class DataTypeMap final : public IDataType TypeIndex getTypeId() const override { return TypeIndex::Map; } std::string doGetName() const override; + std::string doGetPrettyName(size_t indent) const override; const char * getFamilyName() const override { return "Map"; } String getSQLCompatibleName() const override { return "JSON"; } diff --git a/src/DataTypes/DataTypeTuple.cpp b/src/DataTypes/DataTypeTuple.cpp index 768f87fe3d48..fd2e5e6a784a 100644 --- a/src/DataTypes/DataTypeTuple.cpp +++ b/src/DataTypes/DataTypeTuple.cpp @@ -94,6 +94,28 @@ std::string DataTypeTuple::doGetName() const return s.str(); } +std::string DataTypeTuple::doGetPrettyName(size_t indent) const +{ + size_t size = elems.size(); + WriteBufferFromOwnString s; + s << "Tuple(\n"; + + for (size_t i = 0; i != size; ++i) + { + if (i != 0) + s << ",\n"; + + s << fourSpaceIndent(indent + 1); + if (have_explicit_names) + s << backQuoteIfNeed(names[i]) << ' '; + + s << elems[i]->getPrettyName(indent + 1); + } + + s << '\n' << fourSpaceIndent(indent) << ')'; + return s.str(); +} + static inline IColumn & extractElementColumn(IColumn & column, size_t idx) { diff --git a/src/DataTypes/DataTypeTuple.h b/src/DataTypes/DataTypeTuple.h index 0bf3f3ac8b30..0a1e78e23a09 100644 --- a/src/DataTypes/DataTypeTuple.h +++ b/src/DataTypes/DataTypeTuple.h @@ -32,6 +32,7 @@ class DataTypeTuple final : public IDataType TypeIndex getTypeId() const override { return TypeIndex::Tuple; } std::string doGetName() const override; + std::string doGetPrettyName(size_t indent) const override; const char * getFamilyName() const override { return "Tuple"; } String getSQLCompatibleName() const override { return "JSON"; } diff --git a/src/DataTypes/IDataType.cpp b/src/DataTypes/IDataType.cpp index 4ffe82039b28..2a7e0f246de9 100644 --- a/src/DataTypes/IDataType.cpp +++ b/src/DataTypes/IDataType.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/src/DataTypes/IDataType.h b/src/DataTypes/IDataType.h index 782dce116a4c..c30b009c9311 100644 --- a/src/DataTypes/IDataType.h +++ b/src/DataTypes/IDataType.h @@ -65,10 +65,18 @@ class IDataType : private boost::noncopyable, public std::enable_shared_from_thi /// Name of data type (examples: UInt64, Array(String)). String getName() const { - if (custom_name) - return custom_name->getName(); - else - return doGetName(); + if (custom_name) + return custom_name->getName(); + else + return doGetName(); + } + + String getPrettyName(size_t indent = 0) const + { + if (custom_name) + return custom_name->getName(); + else + return doGetPrettyName(indent); } DataTypePtr getPtr() const { return shared_from_this(); } @@ -131,6 +139,8 @@ class IDataType : private boost::noncopyable, public std::enable_shared_from_thi virtual String doGetName() const { return getFamilyName(); } virtual SerializationPtr doGetDefaultSerialization() const = 0; + virtual String doGetPrettyName(size_t /*indent*/) const { return doGetName(); } + public: /** Create empty column for corresponding type and default serialization. */ diff --git a/src/Functions/toTypeName.cpp b/src/Functions/toTypeName.cpp index e66de5f71c6a..b2494fa9094d 100644 --- a/src/Functions/toTypeName.cpp +++ b/src/Functions/toTypeName.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace DB @@ -15,12 +16,15 @@ namespace class FunctionToTypeName : public IFunction { public: + FunctionToTypeName(bool print_pretty_type_names_) : print_pretty_type_names(print_pretty_type_names_) + { + } static constexpr auto name = "toTypeName"; - static FunctionPtr create(ContextPtr) + static FunctionPtr create(ContextPtr context) { - return std::make_shared(); + return std::make_shared(context->getSettingsRef().print_pretty_type_names); } String getName() const override @@ -49,15 +53,18 @@ class FunctionToTypeName : public IFunction ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { - return DataTypeString().createColumnConst(input_rows_count, arguments[0].type->getName()); + return DataTypeString().createColumnConst(input_rows_count, print_pretty_type_names ? arguments[0].type->getPrettyName() : arguments[0].type->getName()); } ColumnPtr getConstantResultForNonConstArguments(const ColumnsWithTypeAndName & arguments, const DataTypePtr &) const override { - return DataTypeString().createColumnConst(1, arguments[0].type->getName()); + return DataTypeString().createColumnConst(1, print_pretty_type_names ? arguments[0].type->getPrettyName() : arguments[0].type->getName()); } ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t /*number_of_arguments*/) const override { return {0}; } + +private: + bool print_pretty_type_names; }; } diff --git a/src/IO/WriteHelpers.cpp b/src/IO/WriteHelpers.cpp index 34eabe55d7ff..9dddcd4b60f5 100644 --- a/src/IO/WriteHelpers.cpp +++ b/src/IO/WriteHelpers.cpp @@ -122,4 +122,8 @@ void writePointerHex(const void * ptr, WriteBuffer & buf) buf.write(hex_str, 2 * sizeof(ptr)); } +String fourSpaceIndent(size_t indent) +{ + return std::string(indent * 4, ' '); +} } diff --git a/src/IO/WriteHelpers.h b/src/IO/WriteHelpers.h index 02a24aeb01f6..58883ff60a9c 100644 --- a/src/IO/WriteHelpers.h +++ b/src/IO/WriteHelpers.h @@ -1405,6 +1405,8 @@ struct PcgSerializer void writePointerHex(const void * ptr, WriteBuffer & buf); +String fourSpaceIndent(size_t indent); + } template<> diff --git a/src/Interpreters/InterpreterDescribeQuery.cpp b/src/Interpreters/InterpreterDescribeQuery.cpp index 26d9e5254f3f..cdca92ab6815 100644 --- a/src/Interpreters/InterpreterDescribeQuery.cpp +++ b/src/Interpreters/InterpreterDescribeQuery.cpp @@ -124,10 +124,16 @@ BlockIO InterpreterDescribeQuery::execute() { res_columns[0]->insert(column.name); + DataTypePtr type; if (extend_object_types) - res_columns[1]->insert(storage_snapshot->getConcreteType(column.name)->getName()); + type = storage_snapshot->getConcreteType(column.name); else - res_columns[1]->insert(column.type->getName()); + type = column.type; + + if (getContext()->getSettingsRef().print_pretty_type_names) + res_columns[1]->insert(type->getPrettyName()); + else + res_columns[1]->insert(type->getName()); if (column.default_desc.expression) { diff --git a/tests/queries/0_stateless/02889_print_pretty_type_names.reference b/tests/queries/0_stateless/02889_print_pretty_type_names.reference new file mode 100644 index 000000000000..ea25df165bb4 --- /dev/null +++ b/tests/queries/0_stateless/02889_print_pretty_type_names.reference @@ -0,0 +1,38 @@ +a Tuple( + b String, + c Tuple( + d Nullable(UInt64), + e Array(UInt32), + f Array(Tuple( + g String, + h Map( + String, + Array(Tuple( + i String, + j UInt64 + )) + ) + )), + k Date + ), + l Nullable(String) +) +Tuple( + b String, + c Tuple( + d Nullable(UInt64), + e Array(UInt32), + f Array(Tuple( + g String, + h Map( + String, + Array(Tuple( + i String, + j UInt64 + )) + ) + )), + k Date + ), + l Nullable(String) +) diff --git a/tests/queries/0_stateless/02889_print_pretty_type_names.sql b/tests/queries/0_stateless/02889_print_pretty_type_names.sql new file mode 100644 index 000000000000..f8a207d357b0 --- /dev/null +++ b/tests/queries/0_stateless/02889_print_pretty_type_names.sql @@ -0,0 +1,5 @@ +create table test (a Tuple(b String, c Tuple(d Nullable(UInt64), e Array(UInt32), f Array(Tuple(g String, h Map(String, Array(Tuple(i String, j UInt64))))), k Date), l Nullable(String))) engine=Memory; +insert into test select * from generateRandom(42) limit 1; +set print_pretty_type_names=1; +desc test format TSVRaw; +select toTypeName(a) from test limit 1 format TSVRaw;