`PRIMARY KEY (key1,key2,key3)` VS `PRIMARY KEY key1,key2,key3`

```
create database test;
use test;

create table dict_table ( key1 UInt64, key2 String, key3 String, S String)
Engine MergeTree order by (key1, key2, key3);

CREATE DICTIONARY item_dict ( key1 UInt64, key2 String, key3 String, S String ) 
PRIMARY KEY (key1,key2,key3) 
SOURCE(CLICKHOUSE(TABLE dict_table DB 'test' USER 'default')) 
LAYOUT(complex_key_direct());

Expected one of: list of elements, identifier

CREATE DICTIONARY item_dict ( key1 UInt64, key2 String, key3 String, S String ) 
PRIMARY KEY key1,key2,key3
SOURCE(CLICKHOUSE(TABLE dict_table DB 'test' USER 'default')) 
LAYOUT(complex_key_direct());
Ok.

0 rows in set. Elapsed: 0.007 sec.
```
{
    ParserKeyValuePairsList key_value_pairs_p;
    ASTPtr ast_range;
    if (!key_value_pairs_p.parse(pos, ast_range, expected))
        return false;
    const ASTExpressionList & expr_list = ast_range->as<const ASTExpressionList &>();
    if (expr_list.children.size() != 2)
        return false;
    auto res = std::make_shared<ASTDictionaryRange>();
    for (const auto & elem : expr_list.children)
    {
        const ASTPair & pair = elem->as<const ASTPair &>();
        const ASTIdentifier * identifier = pair.second->as<ASTIdentifier>();
        if (identifier == nullptr)
            return false;
        if (pair.first == "min")
            res->min_attr_name = identifier->name();
        else if (pair.first == "max")
            res->max_attr_name = identifier->name();
        else
            return false;
    }
    if (res->min_attr_name.empty() || res->max_attr_name.empty())
        return false;
    node = res;
    return true;
}
bool ParserDictionaryLayout::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)