Repository: ClickHouse/ClickHouse
File: src/Parsers/Kusto/KustoFunctions/KQLGeneralFunctions.cpp (C++)

Code before:
    const String fn_name = getKQLFunctionName(pos);
    if (fn_name.empty())
        return false;

    IParser::Pos peek_pos = pos;
    ++peek_pos;
    String original_expr;
    if (peek_pos.isValid() && peek_pos->type != TokenType::Comma && peek_pos->type != TokenType::ClosingRoundBracket)
        original_expr = String(peek_pos->begin, peek_pos->end);

    ++pos;
    String value = getConvertedArgument(fn_name, pos);
    if (value.empty())
        return false;

    ++pos;
    String round_to = getConvertedArgument(fn_name, pos);
    if (round_to.empty())
        return false;

    round_to.erase(std::remove_if(round_to.begin(), round_to.end(), isspace), round_to.end());

    String value_no_spaces = value;
    value_no_spaces.erase(std::remove_if(value_no_spaces.begin(), value_no_spaces.end(), isspace), value_no_spaces.end());
    // Re-parse only timespan-like literals (e.g. 3.5h, 01:02:03). Plain numbers are already normalized.
    if (value_no_spaces.find_first_of(":abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") != String::npos)
    {
        ParserKQLDateTypeTimespan value_tsp;
        if (value_tsp.parseConstKQLTimespan(value_no_spaces))
            value = std::to_string(value_tsp.toSeconds());
    }

    auto t = fmt::format("toFloat64({})", value);

    try
    {
        bin_size = std::stod(round_to);
    }
    catch (const std::exception &)
    {
        ParserKQLDateTypeTimespan time_span_parser;
        if (!time_span_parser.parseConstKQLTimespan(round_to))
            return false;
        bin_size = time_span_parser.toSeconds();
    }

    if (bin_size <= 0)
        return false;

    // Use datetime output whenever first argument is datetime/date (whether bin size is numeric or timespan)
    if (original_expr == "datetime" || original_expr == "date")

Review comment (bug):
this check seems ridiculous - better to attempt to convert it to int first and in case of failure use `parseConstKQLTimespan(value_no_spaces))`

Code after:

    ++pos;

    String original_expr(pos->begin, pos->end);

    String value = getConvertedArgument(fn_name, pos);
    if (value.empty())
        return false;

    ++pos;
    String round_to = getConvertedArgument(fn_name, pos);
    round_to.erase(std::remove_if(round_to.begin(), round_to.end(), [](unsigned char c) { return std::isspace(c); }), round_to.end());
    if (round_to.empty())
        return false;

    String value_no_spaces = value;
    value_no_spaces.erase(std::remove_if(value_no_spaces.begin(), value_no_spaces.end(), [](unsigned char c) { return std::isspace(c); }), value_no_spaces.end());
    if (value_no_spaces.empty())
        return false;
    try
    {
        size_t pos_end = 0;
        std::stod(value_no_spaces, &pos_end);
        if (pos_end != value_no_spaces.size())
            throw std::invalid_argument("not a number");
    }
    catch (const std::exception &)
    {
        ParserKQLDateTypeTimespan value_tsp;
        if (value_tsp.parseConstKQLTimespan(value_no_spaces))
            value = std::to_string(value_tsp.toSeconds());
    }

    auto t = fmt::format("toFloat64({})", value);

    try
    {
        bin_size = std::stod(round_to);
    }
    catch (const std::exception &)
    {
        ParserKQLDateTypeTimespan time_span_parser;
        if (!time_span_parser.parseConstKQLTimespan(round_to))
            return false;
        bin_size = time_span_parser.toSeconds();
    }

    if (bin_size <= 0)
        return false;

    // Use datetime output whenever first argument is datetime/date (whether bin size is numeric or timespan)