**Use case**

I want to estimate better parameters for fulltext (`ngrambf_v1`) index.

**Describe the solution you'd like**

Implement it as SQL function:

`ngrams(string, N)`

that returns array of FixedString.

**Describe alternatives you've considered**

It can be done with less efficient and more cumbersome way:

```
WITH 3 AS n, extractAll(text, '.') AS chars 
SELECT 
  arrayMap(x -> arrayStringConcat(x), 
    arrayFilter(x -> length(x) = n, 
      arrayMap(x, i -> arraySlice(chars, i, n), 
        chars, arrayEnumerate(chars)))) AS ngrams
```

**Additional context**

`ngramsUTF8` can also be implemented to extract ngrams of Unicode code points.
if (!column_ast_identifier)
                return false;
            const auto & map_column_name = column_ast_identifier->name();
            size_t map_keys_key_column_num = 0;
            auto map_keys_index_column_name = fmt::format("mapKeys({})", map_column_name);
            bool map_keys_exists = getKey(map_keys_index_column_name, map_keys_key_column_num);
            size_t map_values_key_column_num = 0;
            auto map_values_index_column_name = fmt::format("mapValues({})", map_column_name);
            bool map_values_exists = getKey(map_values_index_column_name, map_values_key_column_num);
            if (map_keys_exists)
            {
                auto & argument = function->arguments.get()->children[1];
                if (const auto * literal = argument->as<ASTLiteral>())
                {
                    auto element_key = literal->value;
                    const_value = element_key;
                    key_column_num = map_keys_key_column_num;
                    key_exists = true;
                }
                else
                {
                    return false;
                }
            }
            else if (map_values_exists)
            {
                key_column_num = map_values_key_column_num;
                key_exists = true;
            }
            else