Repository: ClickHouse/ClickHouse
File: src/Storages/MergeTree/KeyCondition.cpp (C++)

Code before:

    if (!info.key_subexpr_names.contains(expr_name))
        return false;

    if (out_value.isNull())
        return false;

    MonotonicFunctionsChain transform_functions;
    auto can_transform_constant = extractMonotonicFunctionsChainFromKey(
        node.getTreeContext().getQueryContext(),
        expr_name,
        info,
        out_key_column_num,
        out_key_column_type,
        transform_functions,
        [this](const IFunctionBase & func, const IDataType & type)
        {
            if (!func.hasInformationAboutMonotonicity())
                return false;

            if (!isFunctionReallyMonotonic(func, type))
                return false;

            /// Range is irrelevant in this case.
            auto monotonicity = func.getMonotonicityForRange(type, Field(), Field());
            if (!monotonicity.is_always_monotonic)
                return false;

            return true;
        });

    if (!can_transform_constant)
        return false;

    auto const_column = out_type->createColumnConst(1, out_value);

    ColumnPtr transformed_const_column;
    DataTypePtr transformed_const_type;
    bool constant_transformed = applyFunctionChainToColumn(
        const_column,
        out_type,
        transform_functions,
        transformed_const_column,
        transformed_const_type);

    if (!constant_transformed)
        return false;

    out_value = (*transformed_const_column)[0];
    out_type = transformed_const_type;
    return true;

Review comment (suggestion):
I suspect more functions could be affected by the same problem possibly. Maybe it is worth investing 20 minutes to check.

Code after:
                    return false;
            }
            result_column = result_column_nullable.getNestedColumnPtr();
            result_type = removeNullable(result_type);
        }
    }
    out_column = result_column;
    out_data_type = result_type;

    return true;
}

bool KeyCondition::isFunctionReallyMonotonic(const IFunctionBase & func, const IDataType & arg_type) const
{
    if (date_time_overflow_behavior_ignore && func.getName() == "toDateTime")
    {
        const IDataType * type = &arg_type;
        if (const auto * lowcard_type = typeid_cast<const DataTypeLowCardinality *>(type))
            type = lowcard_type->getDictionaryType().get();
        if (const auto * nullable_type = typeid_cast<const DataTypeNullable *>(type))
            type = nullable_type->getNestedType().get();

        /// toDateTime(date) may overflow, breaking monotonicity.
        if (isDateOrDate32(type))
            return false;
    }

    return true;
}

bool KeyCondition::canConstantBeWrappedByMonotonicFunctions(
    const RPNBuilderTreeNode & node,
    const BuildInfo & info,
    size_t & out_key_column_num,
    DataTypePtr & out_key_column_type,
    Field & out_value,
    DataTypePtr & out_type)
{
    String expr_name = node.getColumnName();

    if (!info.key_subexpr_names.contains(expr_name))
        return false;

    if (out_value.isNull())
        return false;

    MonotonicFunctionsChain transform_functions;
    auto can_transform_constant = extractMonotonicFunctionsChainFromKey(
        node.getTreeContext().getQueryContext(),
        expr_name,
        info,