**Use case**

This works:

```
SELECT * FROM hits_100m_obfuscated INTO OUTFILE 'hits.csv' FORMAT CSV
```

But I want this:

```
SELECT * FROM hits_100m_obfuscated INTO OUTFILE 'hits.csv'
```

**Describe the solution you'd like**

Every format can register file extension(s) in format factory.
If format is not specified explicitly, we can detect it automatically by file extension.
It should work for compressed files `hits.csv.gz` as well.

The following extensions should be supported at least:
csv, tsv, parquet, orc, native, json, ndjson, xml, md, avro

**Addition details**

Let's also support omitting the format argument for `file`, `url`, `s3`, `hdfs` table functions. 
throw NetException(
                    "Unexpected packet from server (expected Data, Exception or Log, got "
                        + String(Protocol::Server::toString(packet.type)) + ")",
                    ErrorCodes::UNEXPECTED_PACKET_FROM_SERVER
        }
    }
}
void ClientBase::processInsertQuery(const String & query_to_execute, ASTPtr parsed_query)
{
    auto query = query_to_execute;
    if (!query_parameters.empty())
    {
        /// Replace ASTQueryParameter with ASTLiteral for prepared statements.
        ReplaceQueryParameterVisitor visitor(query_parameters
        visitor.visit(parsed_query
        /// Get new query after substitutions.
        query = serializeAST(*parsed_query
    }
    /// Process the query that requires transferring data blocks to the server.
    const auto parsed_insert_query = parsed_query->as<ASTInsertQuery &>(
    if ((!parsed_insert_query.data && !parsed_insert_query.infile) && (is_interactive || (!stdin_is_a_tty && std_in.eof())))
        throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT
    connection->sendQuery(
        connection_parameters.timeouts,
        query,
        global_context->getCurrentQueryId(),
        query_processing_stage,
        &global_context->getSettingsRef(),
        &global_context->getClientInfo(),
        true