Skip to content

Commit e383e1a

Browse files
Backport #92842 to 25.11: Fix creating type hint for path 'skip' in JSON
1 parent 30fce2f commit e383e1a

File tree

6 files changed

+67
-7
lines changed

6 files changed

+67
-7
lines changed

src/DataTypes/DataTypeObject.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <Interpreters/Context.h>
2424
#include <Core/Settings.h>
2525
#include <IO/Operators.h>
26+
#include <boost/algorithm/string.hpp>
2627

2728
#include "config.h"
2829

@@ -194,7 +195,11 @@ String DataTypeObject::doGetName() const
194195
for (const auto & path : sorted_typed_paths)
195196
{
196197
write_separator();
197-
out << backQuoteIfNeed(path) << " " << typed_paths.at(path)->getName();
198+
/// We must quote path "SKIP" to avoid its confusion with SKIP keyword.
199+
if (boost::to_upper_copy(path) == "SKIP")
200+
out << backQuote(path) << " " << typed_paths.at(path)->getName();
201+
else
202+
out << backQuoteIfNeed(path) << " " << typed_paths.at(path)->getName();
198203
}
199204

200205
std::vector<String> sorted_skip_paths;
@@ -481,11 +486,18 @@ static DataTypePtr createObject(const ASTPtr & arguments, const DataTypeObject::
481486
}
482487
else if (object_type_argument->path_with_type)
483488
{
484-
const auto * path_with_type = object_type_argument->path_with_type->as<ASTNameTypePair>();
489+
const auto * path_with_type = object_type_argument->path_with_type->as<ASTObjectTypedPathArgument>();
485490
auto data_type = DataTypeFactory::instance().get(path_with_type->type);
486-
if (typed_paths.contains(path_with_type->name))
487-
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Found duplicated path with type: {}", path_with_type->name);
488-
typed_paths.emplace(path_with_type->name, data_type);
491+
if (typed_paths.contains(path_with_type->path))
492+
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Found duplicated path with type: {}", path_with_type->path);
493+
494+
for (const auto & [path, _] : typed_paths)
495+
{
496+
if (path.starts_with(path_with_type->path + ".") || path_with_type->path.starts_with(path + "."))
497+
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Found incompatible typed paths: {} and {}. One of them is a prefix of the other", path, path_with_type->path);
498+
}
499+
500+
typed_paths.emplace(path_with_type->path, data_type);
489501
}
490502
else if (object_type_argument->skip_path)
491503
{

src/Parsers/ASTObjectTypeArgument.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
11
#include <IO/Operators.h>
22
#include <Parsers/ASTObjectTypeArgument.h>
33
#include <Parsers/CommonParsers.h>
4+
#include <Common/quoteString.h>
5+
#include <boost/algorithm/string.hpp>
46

57

68
namespace DB
79
{
810

11+
ASTPtr ASTObjectTypedPathArgument::clone() const
12+
{
13+
auto res = std::make_shared<ASTObjectTypedPathArgument>(*this);
14+
res->children.clear();
15+
16+
if (type)
17+
{
18+
res->type = type->clone();
19+
res->children.push_back(res->type);
20+
}
21+
22+
return res;
23+
}
24+
25+
void ASTObjectTypedPathArgument::formatImpl(
26+
WriteBuffer & ostr, const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
27+
{
28+
/// We must quote path "SKIP" to avoid its confusion with SKIP keyword in Object arguments.
29+
if (boost::to_upper_copy(path) == "SKIP")
30+
ostr << backQuote(path) << ' ';
31+
else
32+
ostr << backQuoteIfNeed(path) << ' ';
33+
34+
type->format(ostr, settings, state, frame);
35+
}
36+
937
ASTPtr ASTObjectTypeArgument::clone() const
1038
{
1139
auto res = std::make_shared<ASTObjectTypeArgument>(*this);

src/Parsers/ASTObjectTypeArgument.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@
66
namespace DB
77
{
88

9+
/// A pair of Object path and its data type. For example: a.b.c String.
10+
class ASTObjectTypedPathArgument : public IAST
11+
{
12+
public:
13+
/// path
14+
String path;
15+
/// type
16+
ASTPtr type;
17+
18+
/** Get the text that identifies this element. */
19+
String getID(char delim) const override { return "ObjectTypedPath" + (delim + path); }
20+
ASTPtr clone() const override;
21+
22+
protected:
23+
void formatImpl(WriteBuffer & ostr, const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
24+
};
25+
926
/** An argument of Object data type declaration (for example for JSON). Can contain one of:
1027
* - pair (path, data type)
1128
* - path that should be skipped

src/Parsers/ParserDataType.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ class ObjectArgumentParser : public IParserBase
110110
if (!type_parser.parse(pos, type, expected))
111111
return false;
112112

113-
auto name_and_type = std::make_shared<ASTNameTypePair>();
114-
name_and_type->name = getIdentifierName(identifier);
113+
auto name_and_type = std::make_shared<ASTObjectTypedPathArgument>();
114+
name_and_type->path = getIdentifierName(identifier);
115115
name_and_type->type = type;
116116
name_and_type->children.push_back(name_and_type->type);
117117
argument->path_with_type = name_and_type;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"skip":"Hello"}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
select '{"skip" : "Hello"}'::JSON(`skip` Nullable(String));
2+

0 commit comments

Comments
 (0)