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

Code before:
            auto insertManyDefaults = [&]()
            {
                MutableColumnPtr to{result_type->createColumn()};
                to->insertManyDefaults(input_rows_count);
                return to;
            };

            /// Expand ColumnConst to full column if needed.
            ColumnPtr object_column = col_const ? col_const->convertToFullColumn() : first_column.column;

            /// Root case (no path specified) return defaults for most functions.
            if (path.empty())
                insertManyDefaults();

            /// Extract literal subcolumn (json.path returns the scalar value at that path).
            auto literal_type = data_type_object.tryGetSubcolumnType(path);
            ColumnPtr literal_subcolumn;
            if (literal_type)
                literal_subcolumn = data_type_object.getSubcolumn(path, object_column);

            /// Extract subobject subcolumn (json.^`path` returns nested object at that path).
            String sub_object_name = "^`" + path + "`";
            auto sub_object_type = data_type_object.tryGetSubcolumnType(sub_object_name);
            ColumnPtr sub_object_subcolumn;
            if (sub_object_type)
                sub_object_subcolumn = data_type_object.getSubcolumn(sub_object_name, object_column);

            /// If neither subcolumn exists, the path is absent thus return defaults.
            if (!literal_subcolumn && !sub_object_subcolumn)
                insertManyDefaults();

            /// Merge literal and subobject subcolumns (same logic as `getObjectElement' in tupleElement.cpp):
            /// For each row: prefer literal (scalar) value, if absent, use subobject (nested JSON).
            ColumnPtr merged;
            DataTypePtr merged_type;

            if (!sub_object_subcolumn
                || sub_object_subcolumn->getNumberOfDefaultRows() == sub_object_subcolumn->size())
            {
                /// No nested subobject at this path just use the literal subcolumn.
                merged = literal_subcolumn;
                merged_type = literal_type;
            }
            else if (!literal_subcolumn
                     || literal_subcolumn->getNumberOfDefaultRows() == literal_subcolumn->size())
            {
                /// No literal value at this path just use the subobject subcolumn.
                merged = sub_object_subcolumn;
                merged_type = sub_object_type;
            }
            else

Review comment (suggestion):
We can use `getSubcolumnType` instead of `tryGetSubcolumnType` and don't check for non-existing subcolumns here, because in JSON type subcolumn will exist for any specified path always.

Code after:
            }

            /// Expand ColumnConst to full column if needed.
            ColumnPtr object_column = col_const ? col_const->convertToFullColumn() : first_column.column;

            /// Root case (no path specified) return defaults for most functions.
            if (path.empty())
                return result_type->createColumnConstWithDefaultValue(input_rows_count)->convertToFullColumnIfConst();

            /// Extract literal subcolumn (json.path returns the scalar value at that path).
            auto literal_type = data_type_object.getSubcolumnType(path);
            auto literal_subcolumn = data_type_object.getSubcolumn(path, object_column);

            ColumnPtr merged;
            DataTypePtr merged_type;

            /// When the path has a type hint (literal_type is not Dynamic), the subobject may not be convertible to that type.
            /// For typed paths we skip the subobject merge and use only the literal subcolumn.
            if (literal_type && !isDynamic(literal_type))
            {
                merged = literal_subcolumn;
                merged_type = literal_type;
            }
            else
            {
                /// Extract subobject subcolumn (json.^`path` returns nested object at that path).
                String sub_object_name = "^`" + path + "`";
                auto sub_object_type = data_type_object.getSubcolumnType(sub_object_name);
                ColumnPtr sub_object_subcolumn = data_type_object.getSubcolumn(sub_object_name, object_column);

                /// Merge literal and subobject subcolumns (same logic as `getObjectElement' in tupleElement.cpp):
                /// For each row: prefer literal (scalar) value, if absent, use subobject (nested JSON).
                if (sub_object_subcolumn->getNumberOfDefaultRows() == sub_object_subcolumn->size())
                {
                    /// No nested subobject at this path (only literal exists).
                    merged = literal_subcolumn;
                    merged_type = literal_type;
                }
                else
                {
                    /// Both exist: merge row-by-row.
                    auto casted_sub_object = castColumn({sub_object_subcolumn, sub_object_type, ""}, literal_type);
                    auto result_col = literal_type->createColumn();
                    for (size_t i = 0; i < input_rows_count; ++i)
                    {
                        if (!literal_subcolumn->isDefaultAt(i))
                            result_col->insertFrom(*literal_subcolumn, i);
                        else if (!sub_object_subcolumn->isDefaultAt(i))
                            result_col->insertFrom(*casted_sub_object, i);
                        else
                            result_col->insertDefault();