```
milovidov-desktop :) CREATE DICTIONARY dict
:-] (
:-]     k1 UInt64,
:-]     k2 UInt8,
:-]     value String
:-] )
:-] PRIMARY KEY k1, k2
:-] SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'dict'))
:-] LIFETIME(1000)
:-] LAYOUT(COMPLEX_KEY_HASHED());

CREATE DICTIONARY dict
(
    `k1` UInt64,
    `k2` UInt8,
    `value` String
)
PRIMARY KEY k1, k2
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'dict'))
LIFETIME(MIN 0 MAX 1000)
LAYOUT(COMPLEX_KEY_HASHED())

Ok.

0 rows in set. Elapsed: 0.001 sec. 

milovidov-desktop :) SELECT dictGetString('default.dict', 'third_column', (1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

SELECT dictGetString('default.dict', 'third_column', (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
```
{
    return createStreamForQuery(query_builder.composeLoadIdsQuery(ids));
}
BlockInputStreamPtr ClickHouseDictionarySource::loadKeys(const Columns & key_columns, const std::vector<size_t> & requested_rows)
{
    String query = query_builder.composeLoadKeysQuery(key_columns, requested_rows, ExternalQueryBuilder::IN_WITH_TUPLES);
    return createStreamForQuery(query);
}
bool ClickHouseDictionarySource::isModified() const
{
    if (!invalidate_query.empty())
    {
        auto response = doInvalidateQuery(invalidate_query);
        LOG_TRACE(log, "Invalidate query has returned: {}, previous value: {}", response, invalidate_query_response);
        if (invalidate_query_response == response)
            return false;
        invalidate_query_response = response;
    }
    return true;
}
bool ClickHouseDictionarySource::hasUpdateField() const
{
    return !update_field.empty();
}
std::string ClickHouseDictionarySource::toString() const
{
    return "ClickHouse: " + db + '.' + table + (where.empty() ? "" : ", where: " + where);
}
BlockInputStreamPtr ClickHouseDictionarySource::createStreamForQuery(const String & query)