aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Functions/JSONPath/Parsers/ParserJSONPathMemberAccess.cpp
blob: 141f25bfe4cc97f74e32363fb824df22592517f0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <Functions/JSONPath/ASTs/ASTJSONPathMemberAccess.h>
#include <Functions/JSONPath/Parsers/ParserJSONPathMemberAccess.h>

#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTIdentifier_fwd.h>
#include <Parsers/ExpressionElementParsers.h>
#include <Parsers/Lexer.h>
#include <Common/StringUtils/StringUtils.h>

namespace DB
{
/**
 *
 * @param pos token iterator
 * @param node node of ASTJSONPathMemberAccess
 * @param expected stuff for logging
 * @return was parse successful
 */
bool ParserJSONPathMemberAccess::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
    // There's a special case, that a path member can begin with number
    // some invalid cases as following
    //   - ".123" is parsed as a number, not a dot and a number
    //   - ".123abc" is parsed as two parts, a number ".123" and a token "abc"
    //   - ".abc" is parsed as two parts. a dot and a token "abc"
    // "$..123abc" is parsed into three parts, ".", ".123" and "abc"
    if (pos->type != TokenType::Dot && pos->type != TokenType::Number)
        return false;
    if (pos->type != TokenType::Number)
    {
        ++pos;
        // Check the case "$..123abc"
        if (pos->type == TokenType::Number)
        {
            return false;
        }
    }

    ASTPtr member_name;

    if (pos->type == TokenType::Number)[[unlikely]]
    {
        for (const auto * c = pos->begin; c != pos->end; ++c)
        {
            if (*c == '.' && c == pos->begin)
                continue;
            if (!isNumericASCII(*c))
            {
                return false;
            }
        }
        const auto * last_begin = *pos->begin == '.' ? pos->begin + 1 : pos->begin;
        const auto * last_end = pos->end;
        ++pos;

        if (pos.isValid() && pos->type == TokenType::BareWord && pos->begin == last_end)
        {
            member_name = std::make_shared<ASTIdentifier>(String(last_begin, pos->end));
            ++pos;
        }
        else if (!pos.isValid() && pos->type == TokenType::EndOfStream)
        {
            member_name = std::make_shared<ASTIdentifier>(String(last_begin, last_end));
        }
        else
        {
            return false;
        }
    }
    else
    {
        if (pos->type != TokenType::BareWord && pos->type != TokenType::QuotedIdentifier)
            return false;

        ParserIdentifier name_p;
        if (!name_p.parse(pos, member_name, expected))
            return false;
    }

    auto member_access = std::make_shared<ASTJSONPathMemberAccess>();
    node = member_access;
    return tryGetIdentifierNameInto(member_name, member_access->member_name);
}

}