May be useful to interface with some external program, e.g. I want to use `clickhouse-format` to beautify the queries for performance test reports.

For now, DDL dictionaries don't work for `clickhouse-local` at all (https://github.com/ClickHouse/ClickHouse/issues/22354)

We should do it through DDL, because the xml config route looks hopeless. `clickhouse-local` can't even load server's `config.xml` properly.
{
    std::unique_ptr<ShellCommand> process;
    bool result = process_pool->tryBorrowObject(process, [this]()
    {
        bool terminate_in_destructor = true;
        ShellCommandDestructorStrategy strategy { terminate_in_destructor, configuration.command_termination_timeout };
        auto shell_command = ShellCommand::execute(configuration.command, false, strategy);
        return shell_command;
    }, configuration.max_command_execution_time * 10000);
    if (!result)
        throw Exception(ErrorCodes::TIMEOUT_EXCEEDED,
            "Could not get process from pool, max command execution timeout exceeded ({}) seconds",
            configuration.max_command_execution_time);
    size_t rows_to_read = block.rows();
    auto read_stream = context->getInputFormat(configuration.format, process->out, sample_block, rows_to_read);
    auto stream = std::make_unique<PoolBlockInputStreamWithBackgroundThread>(
        process_pool, std::move(process), std::move(read_stream), rows_to_read, log,
        [block, this](WriteBufferFromFile & out) mutable
        {
            auto output_stream = context->getOutputStream(configuration.format, out, block.cloneEmpty());
            formatBlock(output_stream, block);
        });
    if (configuration.implicit_key)
        return std::make_shared<BlockInputStreamWithAdditionalColumns>(block, std::move(stream));
    else
        return std::shared_ptr<PoolBlockInputStreamWithBackgroundThread>(stream.release());
}
bool ExecutablePoolDictionarySource::isModified() const