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

Code before:
            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
            {
                /// 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();
                }
                merged = std::move(result_col);
                merged_type = literal_type;
            }

            /// For JSONExtractRaw: serialize each value as a JSON string
            /// using ISerialization::serializeTextJSON.
            constexpr bool is_extract_raw = std::string_view(TName::name) == std::string_view("JSONExtractRaw")
                        || std::string_view(TName::name) == std::string_view("JSONExtractRawCaseInsensitive");

            if constexpr (is_extract_raw)
            {
                auto result_col = ColumnString::create();
                auto serialization = merged_type->getDefaultSerialization();
                for (size_t i = 0; i < input_rows_count; ++i)
                {

Review comment (refactor):
We can have a problem here when requested path has a type hint in JSON, in this case the subobject may be not convertable to the type of path subcolumn. For paths with type hints we don't need this logic with sub-object, because we consider that path with type hint exists in all rows. So we can handle this case separately.

Example:
```sql
select JSONExtract('{"a" : 42, "a.b" : 42}'::JSON(a UInt32), 'a', 'String');
```

And I see that there is the same problem in `tupleElement`. I need to fix it.

Code after:
                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();
                    }
                    merged = std::move(result_col);
                    merged_type = literal_type;
                }
            }

            /// For JSONExtractRaw: serialize each value as a JSON string
            constexpr bool is_extract_raw = std::string_view(TName::name) == std::string_view("JSONExtractRaw")
                        || std::string_view(TName::name) == std::string_view("JSONExtractRawCaseInsensitive");

            if constexpr (is_extract_raw)
            {
                auto raw_col = ColumnString::create();
                auto serialization = merged_type->getDefaultSerialization();
                for (size_t i = 0; i < input_rows_count; ++i)
                {
                    if (merged->isDefaultAt(i))
                    {
                        raw_col->insertDefault();
                    }
                    else
                    {
                        WriteBufferFromOwnString buf;
                        serialization->serializeTextJSON(*merged, i, buf, format_settings);
                        raw_col->insert(buf.str());
                    }
                }
                return raw_col;