If they are defined, they can override table-level settings.
}
    while (!parts_queue.empty())
    {
        assert(!threads_queue.empty());
        size_t i = threads_queue.front();
        auto & need_parts = parts_per_thread[i];
        assert(need_parts > 0);
        auto & thread_parts = threads_parts[i];
        auto & current_parts = parts_queue.front();
        assert(!current_parts.empty());
        auto parts_to_grab = std::min(need_parts, current_parts.size());
        thread_parts.insert(thread_parts.end(), current_parts.end() - parts_to_grab, current_parts.end());
        current_parts.resize(current_parts.size() - parts_to_grab);
        need_parts -= parts_to_grab;
        
        
        
        
        if (!current_parts.empty())
            parts_queue.push(std::move(current_parts));
        parts_queue.pop();
        
        if (need_parts > 0)
            threads_queue.push(i);
        threads_queue.pop();
    }
    assert(threads_queue.empty());
    assert(std::all_of(threads_parts.begin(), threads_parts.end(), [](const std::vector<std::pair<String, DiskPtr>> & parts)
    {
        return !parts.empty();