Repository: ClickHouse/ClickHouse
File: src/Functions/FunctionsJSON.cpp (C++)

Code before:

            const auto & first_column = arguments[0];
            const auto * col_const = typeid_cast<const ColumnConst *>(first_column.column.get());
            const auto * col_object = typeid_cast<const ColumnObject *>(
                col_const ? col_const->getDataColumnPtr().get() : first_column.column.get());

            if (!col_object)
                throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Invalid column type for Object parsing");

            size_t num_index_arguments = TImpl<ColumnObjectParser>::getNumberOfIndexArguments(arguments);
            std::vector<Move> moves = prepareMoves(TName::name, arguments, 1, num_index_arguments);

            TImpl<ColumnObjectParser> impl;
            if constexpr (Preparable<TImpl<ColumnObjectParser>>)
                impl.prepare(TName::name, arguments, result_type);

            using Element = typename ColumnObjectParser::Element;

            /// Build a dotted path from consecutive ConstKey moves for direct flat-key lookup.
            /// ColumnObject stores nested keys in flat form (e.g. "x.y.z") so step-by-step
            /// navigation via performMoves would fail. Instead, join all keys into one path.
            bool all_const_keys = true;
            String flat_path;
            for (const auto & move : moves)
            {
                if (move.type != MoveType::ConstKey)
                {
                    all_const_keys = false;
                    break;
                }
                if (!flat_path.empty())
                    flat_path += '.';
                flat_path += move.key;
            }

            /// Extract last key segment for functions like JSONKey that need it.
            std::string_view last_key;
            if (all_const_keys && !moves.empty())
                last_key = moves.back().key;

            String error;
            for (size_t i = 0; i < input_rows_count; ++i)
            {
                Element document = col_const ? Element(*col_object, 0) : Element(*col_object, i);

                bool added_to_column = false;
                Element element;

                if (all_const_keys)
                {
                    if (flat_path.empty())

Review comment (refactor):
It means that specifing keys indexes won't work at all:
```sql
:) select JSONExtract('{"a" : 42}', 1, 'String')

   ┌─JSONExtract(⋯, 'String')─┐
1. │ 42                       │
   └──────────────────────────┘

:) select JSONExtract('{"a" : 42}'::JSON, 1, 'String')

   ┌─JSONExtract(⋯, 'String')─┐
1. │                          │
   └──────────────────────────┘

```

Code after:
            const DataTypePtr & result_type,
            size_t input_rows_count,
            const FormatSettings & format_settings)
        {
            const auto & first_column = arguments[0];
            const auto & data_type_object = assert_cast<const DataTypeObject &>(*first_column.type);

            const auto * col_const = typeid_cast<const ColumnConst *>(first_column.column.get());
            const auto * col_object = typeid_cast<const ColumnObject *>(
                col_const ? col_const->getDataColumnPtr().get() : first_column.column.get());

            if (!col_object)
                throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Invalid column type for Object parsing");

            /// Determine number of path arguments (parser type doesn't matter for this).
            size_t num_index_arguments = TImpl<DummyJSONParser>::getNumberOfIndexArguments(arguments);

            /// Build a dotted path from constant string key arguments only.
            /// During constant folding (scalar queries without tables), arguments may not be
            /// wrapped in ColumnConst even if they're effectively constant (single-row columns).
            String path;
            for (size_t i = 1; i <= num_index_arguments; ++i)
            {
                const auto & arg = arguments[i];
                if (!isString(arg.type) || !arg.column)
                    throw Exception(
                        ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                        "Function {} with JSON type input supports only constant string path arguments",
                        String(TName::name));

                String key;
                if (const auto * col_const_arg = typeid_cast<const ColumnConst *>(arg.column.get()))
                {
                    key = col_const_arg->getValue<String>();
                }
                else if (input_rows_count <= 1 && !arg.column->empty())
                {
                    /// Constant folding: single-row column that isn't wrapped in ColumnConst.
                    key = (*arg.column)[0].safeGet<String>();
                }
                else
                {
                    throw Exception(
                        ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                        "Function {} with JSON type input supports only constant string path arguments",
                        String(TName::name));
                }

                if (!path.empty())
                    path += '.';
                path += key;